From 473fe21623fd42e406e553228a485d857ec448d9 Mon Sep 17 00:00:00 2001 From: Viktoryia Kliushun Date: Mon, 18 Dec 2023 11:31:27 +0100 Subject: [PATCH 1/8] [TS migration] Migrate 'HeaderWithBackButton' component --- .../{index.js => index.tsx} | 29 +++-- src/components/HeaderWithBackButton/types.ts | 120 ++++++++++++++++++ .../Pressable/GenericPressable/types.ts | 2 +- src/hooks/useSingleExecution/index.ts | 2 + 4 files changed, 139 insertions(+), 14 deletions(-) rename src/components/HeaderWithBackButton/{index.js => index.tsx} (93%) create mode 100644 src/components/HeaderWithBackButton/types.ts diff --git a/src/components/HeaderWithBackButton/index.js b/src/components/HeaderWithBackButton/index.tsx similarity index 93% rename from src/components/HeaderWithBackButton/index.js rename to src/components/HeaderWithBackButton/index.tsx index 5c76d1665310..e172d8567458 100755 --- a/src/components/HeaderWithBackButton/index.js +++ b/src/components/HeaderWithBackButton/index.tsx @@ -18,18 +18,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 = undefined, + iconFill, guidesCallTaskID = '', onBackButtonPress = () => Navigation.goBack(ROUTES.HOME), onCloseButtonPress = () => Navigation.dismissModal(), onDownloadButtonPress = () => {}, onThreeDotsButtonPress = () => {}, report = null, - policy = {}, - personalDetails = {}, + policy, + personalDetails, shouldShowAvatarWithDisplay = false, shouldShowBackButton = true, shouldShowBorderBottom = false, @@ -40,10 +40,10 @@ function HeaderWithBackButton({ shouldShowPinButton = false, shouldShowThreeDotsButton = false, shouldDisableThreeDotsButton = false, - stepCounter = null, + stepCounter, subtitle = '', title = '', - titleColor = undefined, + titleColor, threeDotsAnchorPosition = { vertical: 0, horizontal: 0, @@ -54,13 +54,15 @@ function HeaderWithBackButton({ shouldOverlay = false, singleExecution = (func) => func, shouldNavigateToTopMostReport = false, -}) { +}: HeaderWithBackButtonProps) { const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); const [isDownloadButtonActive, temporarilyDisableDownloadButton] = useThrottledButtonState(); const {translate} = useLocalize(); + // @ts-expect-error TODO: Remove this when useKeyboardState is migrated. const {isKeyboardShown} = useKeyboardState(); const waitForNavigate = useWaitForNavigation(); + return ( { + 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(); + if (event?.currentTarget) { + (event.currentTarget as HTMLElement).blur(); + } if (!isDownloadButtonActive) { return; } onDownloadButtonPress(); - temporarilyDisableDownloadButton(true); + temporarilyDisableDownloadButton(); }} style={[styles.touchableButtonImage]} role="button" @@ -132,7 +136,7 @@ function HeaderWithBackButton({ > @@ -153,7 +157,7 @@ function HeaderWithBackButton({ )} - {shouldShowPinButton && } + {shouldShowPinButton && !!report && } {shouldShowThreeDotsButton && ( ; + + /** Text label */ + text?: string; + + /** A callback triggered when the item is selected */ + onPress?: () => void; +}; + +type HeaderWithBackButtonProps = ChildrenProps & { + /** Title of the Header */ + title?: string; + + /** Subtitle of the header */ + subtitle: string | ReactElement; + + /** 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?: ThreeDotsAnchorPosition; + + /** 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, if we're showing the details for one and using AvatarWithDisplay */ + report?: OnyxEntry; + + /** The report's policy, if we're showing the details for a report and need info about it for AvatarWithDisplay */ + policy?: OnyxEntry; + + /** Policies, if we're showing the details for a report and need participant details for AvatarWithDisplay */ + personalDetails: OnyxCollection; + + /** Single execution function to prevent concurrent navigation actions */ + singleExecution: (action: Action) => Action; + + /** 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; diff --git a/src/components/Pressable/GenericPressable/types.ts b/src/components/Pressable/GenericPressable/types.ts index bff5f651ac9f..cdb9a8624114 100644 --- a/src/components/Pressable/GenericPressable/types.ts +++ b/src/components/Pressable/GenericPressable/types.ts @@ -40,7 +40,7 @@ type PressableProps = RNPressableProps & /** * onPress callback */ - onPress: (event?: GestureResponderEvent | KeyboardEvent) => void; + onPress: (event?: GestureResponderEvent | KeyboardEvent) => void | Promise; /** * Specifies keyboard shortcut to trigger onPressHandler diff --git a/src/hooks/useSingleExecution/index.ts b/src/hooks/useSingleExecution/index.ts index c37087d27c5f..f1be359f0355 100644 --- a/src/hooks/useSingleExecution/index.ts +++ b/src/hooks/useSingleExecution/index.ts @@ -18,3 +18,5 @@ export default function useSingleExecution() { return {isExecuting: false, singleExecution}; } + +export type {Action}; From 0142390bf4810adf003461f2044bed9687438cd4 Mon Sep 17 00:00:00 2001 From: Viktoryia Kliushun Date: Mon, 18 Dec 2023 11:41:05 +0100 Subject: [PATCH 2/8] Update method name --- src/components/HeaderWithBackButton/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/HeaderWithBackButton/types.ts b/src/components/HeaderWithBackButton/types.ts index 334d6b6ca118..7ee6286dfccf 100644 --- a/src/components/HeaderWithBackButton/types.ts +++ b/src/components/HeaderWithBackButton/types.ts @@ -22,7 +22,7 @@ type ThreeDotsMenuItems = { text?: string; /** A callback triggered when the item is selected */ - onPress?: () => void; + onSelected?: () => void; }; type HeaderWithBackButtonProps = ChildrenProps & { From 96d9d39b1808a21eccbaf46cc9a4d95351008b4f Mon Sep 17 00:00:00 2001 From: Viktoryia Kliushun Date: Tue, 19 Dec 2023 08:53:24 +0100 Subject: [PATCH 3/8] Update comment, minor type updates --- src/components/HeaderWithBackButton/index.tsx | 2 +- src/components/HeaderWithBackButton/types.ts | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/HeaderWithBackButton/index.tsx b/src/components/HeaderWithBackButton/index.tsx index e172d8567458..114e15b1a54f 100755 --- a/src/components/HeaderWithBackButton/index.tsx +++ b/src/components/HeaderWithBackButton/index.tsx @@ -59,7 +59,7 @@ function HeaderWithBackButton({ const StyleUtils = useStyleUtils(); const [isDownloadButtonActive, temporarilyDisableDownloadButton] = useThrottledButtonState(); const {translate} = useLocalize(); - // @ts-expect-error TODO: Remove this when useKeyboardState is migrated. + // @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(); diff --git a/src/components/HeaderWithBackButton/types.ts b/src/components/HeaderWithBackButton/types.ts index 7ee6286dfccf..4350e4df8319 100644 --- a/src/components/HeaderWithBackButton/types.ts +++ b/src/components/HeaderWithBackButton/types.ts @@ -8,10 +8,10 @@ import type ChildrenProps from '@src/types/utils/ChildrenProps'; type ThreeDotsAnchorPosition = { /** The vertical anchor position of the three dots menu */ - vertical?: number; + vertical: number; /** The horizontal anchor position of the three dots menu */ - horizontal?: number; + horizontal: number; }; type ThreeDotsMenuItems = { @@ -19,10 +19,10 @@ type ThreeDotsMenuItems = { icon?: React.FC; /** Text label */ - text?: string; + text: string; /** A callback triggered when the item is selected */ - onSelected?: () => void; + onSelected: () => void; }; type HeaderWithBackButtonProps = ChildrenProps & { From dcb0c398bcd2fca73f22de05d05653e92a6847e9 Mon Sep 17 00:00:00 2001 From: Viktoryia Kliushun Date: Tue, 19 Dec 2023 09:15:20 +0100 Subject: [PATCH 4/8] Minor code improvement --- src/components/HeaderWithBackButton/index.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/components/HeaderWithBackButton/index.tsx b/src/components/HeaderWithBackButton/index.tsx index 114e15b1a54f..e01c104d84bc 100755 --- a/src/components/HeaderWithBackButton/index.tsx +++ b/src/components/HeaderWithBackButton/index.tsx @@ -119,9 +119,7 @@ function HeaderWithBackButton({ onPress={(event) => { // Blur the pressable in case this button triggers a Growl notification // We do not want to overlap Growl with the Tooltip (#15271) - if (event?.currentTarget) { - (event.currentTarget as HTMLElement).blur(); - } + (event?.currentTarget as HTMLElement)?.blur(); if (!isDownloadButtonActive) { return; From eedbcf6d8345d3660d69061a8fcfe4405646bbab Mon Sep 17 00:00:00 2001 From: Viktoryia Kliushun Date: Tue, 19 Dec 2023 12:51:39 +0100 Subject: [PATCH 5/8] Reuse existing AnchorPosition type --- src/components/HeaderWithBackButton/types.ts | 11 ++--------- src/styles/index.ts | 2 +- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/src/components/HeaderWithBackButton/types.ts b/src/components/HeaderWithBackButton/types.ts index 4350e4df8319..9955b8a27e3d 100644 --- a/src/components/HeaderWithBackButton/types.ts +++ b/src/components/HeaderWithBackButton/types.ts @@ -3,17 +3,10 @@ import {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import type {SvgProps} from 'react-native-svg'; 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'; -type ThreeDotsAnchorPosition = { - /** The vertical anchor position of the three dots menu */ - vertical: number; - - /** The horizontal anchor position of the three dots menu */ - horizontal: number; -}; - type ThreeDotsMenuItems = { /** An icon element displayed on the left side */ icon?: React.FC; @@ -72,7 +65,7 @@ type HeaderWithBackButtonProps = ChildrenProps & { threeDotsMenuItems?: ThreeDotsMenuItems[]; /** The anchor position of the menu */ - threeDotsAnchorPosition?: ThreeDotsAnchorPosition; + threeDotsAnchorPosition?: AnchorPosition; /** Whether we should show a close button */ shouldShowCloseButton?: boolean; diff --git a/src/styles/index.ts b/src/styles/index.ts index 5227e87f34c7..204274654b64 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -4079,4 +4079,4 @@ const defaultStyles = styles(defaultTheme); export default styles; export {defaultStyles}; -export type {Styles, ThemeStyles, StatusBarStyle, ColorScheme}; +export type {Styles, ThemeStyles, StatusBarStyle, ColorScheme, AnchorPosition}; From 19426254d4d84e49afed6c27a5929e0d36e1497f Mon Sep 17 00:00:00 2001 From: Viktoryia Kliushun Date: Wed, 27 Dec 2023 10:34:03 +0100 Subject: [PATCH 6/8] TS fixes after merging main --- src/components/HeaderWithBackButton/index.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/HeaderWithBackButton/index.tsx b/src/components/HeaderWithBackButton/index.tsx index 8732b833fdc8..40ed5f8e91b4 100755 --- a/src/components/HeaderWithBackButton/index.tsx +++ b/src/components/HeaderWithBackButton/index.tsx @@ -22,7 +22,7 @@ import ROUTES from '@src/ROUTES'; import HeaderWithBackButtonProps from './types'; function HeaderWithBackButton({ - iconFill = null, + iconFill, guidesCallTaskID = '', onBackButtonPress = () => Navigation.goBack(ROUTES.HOME), onCloseButtonPress = () => Navigation.dismissModal(), @@ -94,7 +94,7 @@ function HeaderWithBackButton({ > @@ -152,7 +152,7 @@ function HeaderWithBackButton({ > @@ -177,7 +177,7 @@ function HeaderWithBackButton({ > From 654e903fbbc266332619e405561ceb3df63c918e Mon Sep 17 00:00:00 2001 From: Viktoryia Kliushun Date: Fri, 29 Dec 2023 11:35:35 +0100 Subject: [PATCH 7/8] Use IconAsset type --- src/components/HeaderWithBackButton/types.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/HeaderWithBackButton/types.ts b/src/components/HeaderWithBackButton/types.ts index 9955b8a27e3d..5bb2428e4778 100644 --- a/src/components/HeaderWithBackButton/types.ts +++ b/src/components/HeaderWithBackButton/types.ts @@ -1,15 +1,15 @@ -import React, {ReactElement} from 'react'; +import {ReactElement} from 'react'; import {OnyxCollection, OnyxEntry} from 'react-native-onyx'; -import type {SvgProps} from 'react-native-svg'; 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?: React.FC; + icon?: IconAsset; /** Text label */ text: string; From 1e8b542b25d7f7ae012ba84613b89aeb4ef445c9 Mon Sep 17 00:00:00 2001 From: Viktoryia Kliushun Date: Fri, 29 Dec 2023 12:00:50 +0100 Subject: [PATCH 8/8] Update Header props type --- src/components/Header.tsx | 6 +++--- src/components/HeaderWithBackButton/index.tsx | 2 +- src/components/HeaderWithBackButton/types.ts | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/components/Header.tsx b/src/components/Header.tsx index c02f21d7c6f2..4eac2c7a6994 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -1,4 +1,4 @@ -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'; @@ -6,10 +6,10 @@ 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; diff --git a/src/components/HeaderWithBackButton/index.tsx b/src/components/HeaderWithBackButton/index.tsx index 40ed5f8e91b4..9ec8bca55a95 100755 --- a/src/components/HeaderWithBackButton/index.tsx +++ b/src/components/HeaderWithBackButton/index.tsx @@ -30,7 +30,7 @@ function HeaderWithBackButton({ onThreeDotsButtonPress = () => {}, report = null, policy, - personalDetails, + personalDetails = null, shouldShowAvatarWithDisplay = false, shouldShowBackButton = true, shouldShowBorderBottom = false, diff --git a/src/components/HeaderWithBackButton/types.ts b/src/components/HeaderWithBackButton/types.ts index 5bb2428e4778..939b2530fa3d 100644 --- a/src/components/HeaderWithBackButton/types.ts +++ b/src/components/HeaderWithBackButton/types.ts @@ -1,4 +1,4 @@ -import {ReactElement} from 'react'; +import {ReactNode} from 'react'; import {OnyxCollection, OnyxEntry} from 'react-native-onyx'; import type {Action} from '@hooks/useSingleExecution'; import type {StepCounterParams} from '@src/languages/types'; @@ -23,7 +23,7 @@ type HeaderWithBackButtonProps = ChildrenProps & { title?: string; /** Subtitle of the header */ - subtitle: string | ReactElement; + subtitle?: ReactNode; /** Title color */ titleColor?: string; @@ -92,10 +92,10 @@ type HeaderWithBackButtonProps = ChildrenProps & { policy?: OnyxEntry; /** Policies, if we're showing the details for a report and need participant details for AvatarWithDisplay */ - personalDetails: OnyxCollection; + personalDetails?: OnyxCollection; /** Single execution function to prevent concurrent navigation actions */ - singleExecution: (action: Action) => Action; + singleExecution?: (action: Action) => Action; /** Whether we should navigate to report page when the route have a topMostReport */ shouldNavigateToTopMostReport?: boolean;