Skip to content

Commit

Permalink
Merge pull request Expensify#33214 from VickyStash/ts-migration/heade…
Browse files Browse the repository at this point in the history
…rWithBackButton-component

[TS migration] Migrate 'HeaderWithBackButton' component to TypeScript
  • Loading branch information
mountiny authored Dec 29, 2023
2 parents d4e0db5 + 1e8b542 commit 4a7014b
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 20 deletions.
6 changes: 3 additions & 3 deletions src/components/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import React, {ReactElement} from 'react';
import React, {ReactNode} from 'react';
import {StyleProp, TextStyle, View} from 'react-native';
import useThemeStyles from '@hooks/useThemeStyles';
import EnvironmentBadge from './EnvironmentBadge';
import Text from './Text';

type HeaderProps = {
/** Title of the Header */
title?: string | ReactElement;
title?: ReactNode;

/** Subtitle of the header */
subtitle?: string | ReactElement;
subtitle?: ReactNode;

/** Should we show the environment badge (dev/stg)? */
shouldShowEnvironmentBadge?: boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,18 @@ import getButtonState from '@libs/getButtonState';
import Navigation from '@libs/Navigation/Navigation';
import CONST from '@src/CONST';
import ROUTES from '@src/ROUTES';
import headerWithBackButtonPropTypes from './headerWithBackButtonPropTypes';
import HeaderWithBackButtonProps from './types';

function HeaderWithBackButton({
iconFill = null,
iconFill,
guidesCallTaskID = '',
onBackButtonPress = () => Navigation.goBack(ROUTES.HOME),
onCloseButtonPress = () => Navigation.dismissModal(),
onDownloadButtonPress = () => {},
onThreeDotsButtonPress = () => {},
report = null,
policy = {},
personalDetails = {},
policy,
personalDetails = null,
shouldShowAvatarWithDisplay = false,
shouldShowBackButton = true,
shouldShowBorderBottom = false,
Expand All @@ -41,10 +41,10 @@ function HeaderWithBackButton({
shouldShowPinButton = false,
shouldShowThreeDotsButton = false,
shouldDisableThreeDotsButton = false,
stepCounter = null,
stepCounter,
subtitle = '',
title = '',
titleColor = undefined,
titleColor,
threeDotsAnchorPosition = {
vertical: 0,
horizontal: 0,
Expand All @@ -55,14 +55,16 @@ function HeaderWithBackButton({
shouldOverlay = false,
singleExecution = (func) => func,
shouldNavigateToTopMostReport = false,
}) {
}: HeaderWithBackButtonProps) {
const theme = useTheme();
const styles = useThemeStyles();
const StyleUtils = useStyleUtils();
const [isDownloadButtonActive, temporarilyDisableDownloadButton] = useThrottledButtonState();
const {translate} = useLocalize();
// @ts-expect-error TODO: Remove this once useKeyboardState (https://github.com/Expensify/App/issues/24941) is migrated to TypeScript.
const {isKeyboardShown} = useKeyboardState();
const waitForNavigate = useWaitForNavigation();

return (
<View
// Hover on some part of close icons will not work on Electron if dragArea is true
Expand Down Expand Up @@ -92,7 +94,7 @@ function HeaderWithBackButton({
>
<Icon
src={Expensicons.BackArrow}
fill={iconFill || theme.icon}
fill={iconFill ?? theme.icon}
/>
</PressableWithoutFeedback>
</Tooltip>
Expand All @@ -116,25 +118,25 @@ function HeaderWithBackButton({
{shouldShowDownloadButton && (
<Tooltip text={translate('common.download')}>
<PressableWithoutFeedback
onPress={(e) => {
onPress={(event) => {
// Blur the pressable in case this button triggers a Growl notification
// We do not want to overlap Growl with the Tooltip (#15271)
e.currentTarget.blur();
(event?.currentTarget as HTMLElement)?.blur();

if (!isDownloadButtonActive) {
return;
}

onDownloadButtonPress();
temporarilyDisableDownloadButton(true);
temporarilyDisableDownloadButton();
}}
style={[styles.touchableButtonImage]}
role="button"
accessibilityLabel={translate('common.download')}
>
<Icon
src={Expensicons.Download}
fill={iconFill || StyleUtils.getIconFillColor(getButtonState(false, false, !isDownloadButtonActive))}
fill={iconFill ?? StyleUtils.getIconFillColor(getButtonState(false, false, !isDownloadButtonActive))}
/>
</PressableWithoutFeedback>
</Tooltip>
Expand All @@ -150,12 +152,12 @@ function HeaderWithBackButton({
>
<Icon
src={Expensicons.QuestionMark}
fill={iconFill || theme.icon}
fill={iconFill ?? theme.icon}
/>
</PressableWithoutFeedback>
</Tooltip>
)}
{shouldShowPinButton && <PinButton report={report} />}
{shouldShowPinButton && !!report && <PinButton report={report} />}
{shouldShowThreeDotsButton && (
<ThreeDotsMenu
disabled={shouldDisableThreeDotsButton}
Expand All @@ -175,7 +177,7 @@ function HeaderWithBackButton({
>
<Icon
src={Expensicons.Close}
fill={iconFill || theme.icon}
fill={iconFill ?? theme.icon}
/>
</PressableWithoutFeedback>
</Tooltip>
Expand All @@ -186,7 +188,6 @@ function HeaderWithBackButton({
);
}

HeaderWithBackButton.propTypes = headerWithBackButtonPropTypes;
HeaderWithBackButton.displayName = 'HeaderWithBackButton';

export default HeaderWithBackButton;
113 changes: 113 additions & 0 deletions src/components/HeaderWithBackButton/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import {ReactNode} from 'react';
import {OnyxCollection, OnyxEntry} from 'react-native-onyx';
import type {Action} from '@hooks/useSingleExecution';
import type {StepCounterParams} from '@src/languages/types';
import type {AnchorPosition} from '@src/styles';
import type {PersonalDetails, Policy, Report} from '@src/types/onyx';
import type ChildrenProps from '@src/types/utils/ChildrenProps';
import type IconAsset from '@src/types/utils/IconAsset';

type ThreeDotsMenuItems = {
/** An icon element displayed on the left side */
icon?: IconAsset;

/** Text label */
text: string;

/** A callback triggered when the item is selected */
onSelected: () => void;
};

type HeaderWithBackButtonProps = ChildrenProps & {
/** Title of the Header */
title?: string;

/** Subtitle of the header */
subtitle?: ReactNode;

/** Title color */
titleColor?: string;

/** Method to trigger when pressing download button of the header */
onDownloadButtonPress?: () => void;

/** Method to trigger when pressing close button of the header */
onCloseButtonPress?: () => void;

/** Method to trigger when pressing back button of the header */
onBackButtonPress?: () => void;

/** Method to trigger when pressing more options button of the header */
onThreeDotsButtonPress?: () => void;

/** Whether we should show a border on the bottom of the Header */
shouldShowBorderBottom?: boolean;

/** Whether we should show a download button */
shouldShowDownloadButton?: boolean;

/** Whether we should show a get assistance (question mark) button */
shouldShowGetAssistanceButton?: boolean;

/** Whether we should disable the get assistance button */
shouldDisableGetAssistanceButton?: boolean;

/** Whether we should show a pin button */
shouldShowPinButton?: boolean;

/** Whether we should show a more options (threedots) button */
shouldShowThreeDotsButton?: boolean;

/** Whether we should disable threedots button */
shouldDisableThreeDotsButton?: boolean;

/** List of menu items for more(three dots) menu */
threeDotsMenuItems?: ThreeDotsMenuItems[];

/** The anchor position of the menu */
threeDotsAnchorPosition?: AnchorPosition;

/** Whether we should show a close button */
shouldShowCloseButton?: boolean;

/** Whether we should show a back button */
shouldShowBackButton?: boolean;

/** The guides call taskID to associate with the get assistance button, if we show it */
guidesCallTaskID?: string;

/** Data to display a step counter in the header */
stepCounter?: StepCounterParams;

/** Whether we should show an avatar */
shouldShowAvatarWithDisplay?: boolean;

/** Parent report, if provided it will override props.report for AvatarWithDisplay */
parentReport?: OnyxEntry<Report>;

/** Report, if we're showing the details for one and using AvatarWithDisplay */
report?: OnyxEntry<Report>;

/** The report's policy, if we're showing the details for a report and need info about it for AvatarWithDisplay */
policy?: OnyxEntry<Policy>;

/** Policies, if we're showing the details for a report and need participant details for AvatarWithDisplay */
personalDetails?: OnyxCollection<PersonalDetails>;

/** Single execution function to prevent concurrent navigation actions */
singleExecution?: <T extends unknown[]>(action: Action<T>) => Action<T>;

/** Whether we should navigate to report page when the route have a topMostReport */
shouldNavigateToTopMostReport?: boolean;

/** The fill color for the icon. Can be hex, rgb, rgba, or valid react-native named color such as 'red' or 'blue'. */
iconFill?: string;

/** Whether the popover menu should overlay the current view */
shouldOverlay?: boolean;

/** Whether we should enable detail page navigation */
shouldEnableDetailPageNavigation?: boolean;
};

export default HeaderWithBackButtonProps;
2 changes: 1 addition & 1 deletion src/components/Pressable/GenericPressable/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ type PressableProps = RNPressableProps &
/**
* onPress callback
*/
onPress: (event?: GestureResponderEvent | KeyboardEvent) => void;
onPress: (event?: GestureResponderEvent | KeyboardEvent) => void | Promise<void>;

/**
* Specifies keyboard shortcut to trigger onPressHandler
Expand Down
2 changes: 2 additions & 0 deletions src/hooks/useSingleExecution/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,5 @@ export default function useSingleExecution() {

return {isExecuting: false, singleExecution};
}

export type {Action};

0 comments on commit 4a7014b

Please sign in to comment.