From 32f95b62675a9e868a6ab01ea653657d3a53a5e9 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Mon, 11 Dec 2023 16:17:06 +0100 Subject: [PATCH 1/8] [TS migration] Migrate 'VideoChatButtonAndMenu' component to TypeScript --- src/components/Popover/types.ts | 15 +++---- src/components/PopoverProvider/index.tsx | 39 +++++++++------- src/components/PopoverProvider/types.ts | 13 +++--- ...Menu.js => BaseVideoChatButtonAndMenu.tsx} | 44 ++++++++----------- .../{index.android.js => index.android.tsx} | 7 ++- .../{index.js => index.tsx} | 7 ++- .../VideoChatButtonAndMenu/types.ts | 9 ++++ .../videoChatButtonAndMenuPropTypes.js | 16 ------- 8 files changed, 71 insertions(+), 79 deletions(-) rename src/components/VideoChatButtonAndMenu/{BaseVideoChatButtonAndMenu.js => BaseVideoChatButtonAndMenu.tsx} (75%) rename src/components/VideoChatButtonAndMenu/{index.android.js => index.android.tsx} (76%) rename src/components/VideoChatButtonAndMenu/{index.js => index.tsx} (68%) create mode 100644 src/components/VideoChatButtonAndMenu/types.ts delete mode 100644 src/components/VideoChatButtonAndMenu/videoChatButtonAndMenuPropTypes.js diff --git a/src/components/Popover/types.ts b/src/components/Popover/types.ts index 7f7e2829770c..103ab0404081 100644 --- a/src/components/Popover/types.ts +++ b/src/components/Popover/types.ts @@ -1,8 +1,8 @@ +import {ReactNode, RefObject} from 'react'; +import {View} from 'react-native'; import BaseModalProps, {PopoverAnchorPosition} from '@components/Modal/types'; import {WindowDimensionsProps} from '@components/withWindowDimensions/types'; -type AnchorAlignment = {horizontal: string; vertical: string}; - type PopoverDimensions = { width: number; height: number; @@ -12,14 +12,11 @@ type PopoverProps = BaseModalProps & { /** The anchor position of the popover */ anchorPosition?: PopoverAnchorPosition; - /** The anchor alignment of the popover */ - anchorAlignment: AnchorAlignment; - /** The anchor ref of the popover */ - anchorRef: React.RefObject; + anchorRef: RefObject; /** Whether disable the animations */ - disableAnimation: boolean; + disableAnimation?: boolean; /** Whether we don't want to show overlay */ withoutOverlay: boolean; @@ -28,13 +25,13 @@ type PopoverProps = BaseModalProps & { popoverDimensions?: PopoverDimensions; /** The ref of the popover */ - withoutOverlayRef?: React.RefObject; + withoutOverlayRef?: RefObject; /** Whether we want to show the popover on the right side of the screen */ fromSidebarMediumScreen?: boolean; /** The popover children */ - children: React.ReactNode; + children: ReactNode; }; type PopoverWithWindowDimensionsProps = PopoverProps & WindowDimensionsProps; diff --git a/src/components/PopoverProvider/index.tsx b/src/components/PopoverProvider/index.tsx index 06345ebdbc1c..1be7c7a95bc1 100644 --- a/src/components/PopoverProvider/index.tsx +++ b/src/components/PopoverProvider/index.tsx @@ -1,18 +1,26 @@ -import React from 'react'; +import React, {createContext, RefObject, useCallback, useEffect, useMemo, useRef, useState} from 'react'; +import {View} from 'react-native'; import {AnchorRef, PopoverContextProps, PopoverContextValue} from './types'; -const PopoverContext = React.createContext({ +const PopoverContext = createContext({ onOpen: () => {}, popover: {}, close: () => {}, isOpen: false, }); +function elementContains(ref: RefObject | undefined, target: EventTarget | null) { + if (ref?.current && 'contains' in ref?.current && ref?.current?.contains(target as Node)) { + return true; + } + return false; +} + function PopoverContextProvider(props: PopoverContextProps) { - const [isOpen, setIsOpen] = React.useState(false); - const activePopoverRef = React.useRef(null); + const [isOpen, setIsOpen] = useState(false); + const activePopoverRef = useRef(null); - const closePopover = React.useCallback((anchorRef?: React.RefObject) => { + const closePopover = useCallback((anchorRef?: RefObject) => { if (!activePopoverRef.current || (anchorRef && anchorRef !== activePopoverRef.current.anchorRef)) { return; } @@ -25,10 +33,9 @@ function PopoverContextProvider(props: PopoverContextProps) { setIsOpen(false); }, []); - React.useEffect(() => { + useEffect(() => { const listener = (e: Event) => { - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - if (activePopoverRef.current?.ref?.current?.contains(e.target as Node) || activePopoverRef.current?.anchorRef?.current?.contains(e.target as Node)) { + if (elementContains(activePopoverRef.current?.ref, e.target) || elementContains(activePopoverRef.current?.anchorRef, e.target)) { return; } const ref = activePopoverRef.current?.anchorRef; @@ -40,9 +47,9 @@ function PopoverContextProvider(props: PopoverContextProps) { }; }, [closePopover]); - React.useEffect(() => { + useEffect(() => { const listener = (e: Event) => { - if (activePopoverRef.current?.ref?.current?.contains(e.target as Node)) { + if (elementContains(activePopoverRef.current?.ref, e.target)) { return; } closePopover(); @@ -53,7 +60,7 @@ function PopoverContextProvider(props: PopoverContextProps) { }; }, [closePopover]); - React.useEffect(() => { + useEffect(() => { const listener = (e: KeyboardEvent) => { if (e.key !== 'Escape') { return; @@ -66,7 +73,7 @@ function PopoverContextProvider(props: PopoverContextProps) { }; }, [closePopover]); - React.useEffect(() => { + useEffect(() => { const listener = () => { if (document.hasFocus()) { return; @@ -79,9 +86,9 @@ function PopoverContextProvider(props: PopoverContextProps) { }; }, [closePopover]); - React.useEffect(() => { + useEffect(() => { const listener = (e: Event) => { - if (activePopoverRef.current?.ref?.current?.contains(e.target as Node)) { + if (elementContains(activePopoverRef.current?.ref, e.target)) { return; } @@ -93,7 +100,7 @@ function PopoverContextProvider(props: PopoverContextProps) { }; }, [closePopover]); - const onOpen = React.useCallback( + const onOpen = useCallback( (popoverParams: AnchorRef) => { if (activePopoverRef.current && activePopoverRef.current.ref !== popoverParams?.ref) { closePopover(activePopoverRef.current.anchorRef); @@ -107,7 +114,7 @@ function PopoverContextProvider(props: PopoverContextProps) { [closePopover], ); - const contextValue = React.useMemo( + const contextValue = useMemo( () => ({ onOpen, close: closePopover, diff --git a/src/components/PopoverProvider/types.ts b/src/components/PopoverProvider/types.ts index ffd0087cd5ff..dc0208e10dd7 100644 --- a/src/components/PopoverProvider/types.ts +++ b/src/components/PopoverProvider/types.ts @@ -1,18 +1,21 @@ +import {ReactNode, RefObject} from 'react'; +import {View} from 'react-native'; + type PopoverContextProps = { - children: React.ReactNode; + children: ReactNode; }; type PopoverContextValue = { onOpen?: (popoverParams: AnchorRef) => void; popover?: AnchorRef | Record | null; - close: (anchorRef?: React.RefObject) => void; + close: (anchorRef?: RefObject) => void; isOpen: boolean; }; type AnchorRef = { - ref: React.RefObject; - close: (anchorRef?: React.RefObject) => void; - anchorRef: React.RefObject; + ref: RefObject; + close: (anchorRef?: RefObject) => void; + anchorRef: RefObject; onOpenCallback?: () => void; onCloseCallback?: () => void; }; diff --git a/src/components/VideoChatButtonAndMenu/BaseVideoChatButtonAndMenu.js b/src/components/VideoChatButtonAndMenu/BaseVideoChatButtonAndMenu.tsx similarity index 75% rename from src/components/VideoChatButtonAndMenu/BaseVideoChatButtonAndMenu.js rename to src/components/VideoChatButtonAndMenu/BaseVideoChatButtonAndMenu.tsx index 4d5affafc407..14dbcb9a118b 100755 --- a/src/components/VideoChatButtonAndMenu/BaseVideoChatButtonAndMenu.js +++ b/src/components/VideoChatButtonAndMenu/BaseVideoChatButtonAndMenu.tsx @@ -1,7 +1,5 @@ -import PropTypes from 'prop-types'; import React, {useCallback, useEffect, useRef, useState} from 'react'; import {Dimensions, View} from 'react-native'; -import _ from 'underscore'; import GoogleMeetIcon from '@assets/images/google-meet.svg'; import ZoomIcon from '@assets/images/zoom-icon.svg'; import Icon from '@components/Icon'; @@ -10,37 +8,34 @@ import MenuItem from '@components/MenuItem'; import Popover from '@components/Popover'; import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback'; import Tooltip from '@components/Tooltip/PopoverAnchorTooltip'; -import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; -import withWindowDimensions, {windowDimensionsPropTypes} from '@components/withWindowDimensions'; -import compose from '@libs/compose'; +import useLocalize from '@hooks/useLocalize'; +import useWindowDimensions from '@hooks/useWindowDimensions'; import useTheme from '@styles/themes/useTheme'; import useThemeStyles from '@styles/useThemeStyles'; import * as Link from '@userActions/Link'; import * as Session from '@userActions/Session'; import CONST from '@src/CONST'; -import {defaultProps, propTypes as videoChatButtonAndMenuPropTypes} from './videoChatButtonAndMenuPropTypes'; +import VideoChatButtonAndMenuProps from './types'; -const propTypes = { +type BaseVideoChatButtonAndMenuProps = VideoChatButtonAndMenuProps & { /** Link to open when user wants to create a new google meet meeting */ - googleMeetURL: PropTypes.string.isRequired, - - ...videoChatButtonAndMenuPropTypes, - ...withLocalizePropTypes, - ...windowDimensionsPropTypes, + googleMeetURL: string; }; -function BaseVideoChatButtonAndMenu(props) { +function BaseVideoChatButtonAndMenu(props: BaseVideoChatButtonAndMenuProps) { const theme = useTheme(); const styles = useThemeStyles(); + const {translate} = useLocalize(); + const {isSmallScreenWidth} = useWindowDimensions(); const [isVideoChatMenuActive, setIsVideoChatMenuActive] = useState(false); const [videoChatIconPosition, setVideoChatIconPosition] = useState({x: 0, y: 0}); - const videoChatIconWrapperRef = useRef(null); - const videoChatButtonRef = useRef(null); + const videoChatIconWrapperRef = useRef(null); + const videoChatButtonRef = useRef(null); const menuItemData = [ { icon: ZoomIcon, - text: props.translate('videoChatButtonAndMenu.zoom'), + text: translate('videoChatButtonAndMenu.zoom'), onPress: () => { setIsVideoChatMenuActive(false); Link.openExternalLink(CONST.NEW_ZOOM_MEETING_URL); @@ -48,7 +43,7 @@ function BaseVideoChatButtonAndMenu(props) { }, { icon: GoogleMeetIcon, - text: props.translate('videoChatButtonAndMenu.googleMeet'), + text: translate('videoChatButtonAndMenu.googleMeet'), onPress: () => { setIsVideoChatMenuActive(false); Link.openExternalLink(props.googleMeetURL); @@ -87,12 +82,12 @@ function BaseVideoChatButtonAndMenu(props) { ref={videoChatIconWrapperRef} onLayout={measureVideoChatIconPosition} > - + { // Drop focus to avoid blue focus ring. - videoChatButtonRef.current.blur(); + videoChatButtonRef.current?.blur(); // If this is the Concierge chat, we'll open the modal for requesting a setup call instead if (props.isConcierge && props.guideCalendarLink) { @@ -102,7 +97,7 @@ function BaseVideoChatButtonAndMenu(props) { setIsVideoChatMenuActive((previousVal) => !previousVal); })} style={styles.touchableButtonImage} - accessibilityLabel={props.translate('videoChatButtonAndMenu.tooltip')} + accessibilityLabel={translate('videoChatButtonAndMenu.tooltip')} role={CONST.ACCESSIBILITY_ROLE.BUTTON} > - - {_.map(menuItemData, ({icon, text, onPress}) => ( + + {menuItemData.map(({icon, text, onPress}) => ( Date: Tue, 2 Jan 2024 16:30:30 +0100 Subject: [PATCH 2/8] Fix MenuItem types --- src/components/MenuItem.tsx | 245 +++++++++--------- .../BaseVideoChatButtonAndMenu.tsx | 5 +- 2 files changed, 121 insertions(+), 129 deletions(-) diff --git a/src/components/MenuItem.tsx b/src/components/MenuItem.tsx index db150d55f0d2..2b192aba2f2e 100644 --- a/src/components/MenuItem.tsx +++ b/src/components/MenuItem.tsx @@ -33,20 +33,6 @@ import RenderHTML from './RenderHTML'; import SelectCircle from './SelectCircle'; import Text from './Text'; -type ResponsiveProps = { - /** Function to fire when component is pressed */ - onPress: (event: GestureResponderEvent | KeyboardEvent) => void; - - interactive?: true; -}; - -type UnresponsiveProps = { - onPress?: undefined; - - /** Whether the menu item should be interactive at all */ - interactive: false; -}; - type IconProps = { /** Flag to choose between avatar image or an icon */ iconType: typeof CONST.ICON_TYPE_ICON; @@ -56,7 +42,7 @@ type IconProps = { }; type AvatarProps = { - iconType: typeof CONST.ICON_TYPE_AVATAR | typeof CONST.ICON_TYPE_WORKSPACE; + iconType?: typeof CONST.ICON_TYPE_AVATAR | typeof CONST.ICON_TYPE_WORKSPACE; icon: AvatarSource; }; @@ -67,170 +53,175 @@ type NoIcon = { icon?: undefined; }; -type MenuItemProps = (ResponsiveProps | UnresponsiveProps) & - (IconProps | AvatarProps | NoIcon) & { - /** Text to be shown as badge near the right end. */ - badgeText?: string; +type MenuItemProps = (IconProps | AvatarProps | NoIcon) & { + /** Function to fire when component is pressed */ + onPress?: (event: GestureResponderEvent | KeyboardEvent) => void; - /** Used to apply offline styles to child text components */ - style?: ViewStyle; + /** Whether the menu item should be interactive at all */ + interactive?: boolean; - /** Any additional styles to apply */ - wrapperStyle?: StyleProp; + /** Text to be shown as badge near the right end. */ + badgeText?: string; - /** Any additional styles to apply on the outer element */ - containerStyle?: StyleProp; + /** Used to apply offline styles to child text components */ + style?: StyleProp; - /** Used to apply styles specifically to the title */ - titleStyle?: ViewStyle; + /** Any additional styles to apply */ + wrapperStyle?: StyleProp; - /** Any adjustments to style when menu item is hovered or pressed */ - hoverAndPressStyle: StyleProp>; + /** Any additional styles to apply on the outer element */ + containerStyle?: StyleProp; - /** Additional styles to style the description text below the title */ - descriptionTextStyle?: StyleProp; + /** Used to apply styles specifically to the title */ + titleStyle?: ViewStyle; - /** The fill color to pass into the icon. */ - iconFill?: string; + /** Any adjustments to style when menu item is hovered or pressed */ + hoverAndPressStyle?: StyleProp>; - /** Secondary icon to display on the left side of component, right of the icon */ - secondaryIcon?: IconAsset; + /** Additional styles to style the description text below the title */ + descriptionTextStyle?: StyleProp; - /** The fill color to pass into the secondary icon. */ - secondaryIconFill?: string; + /** The fill color to pass into the icon. */ + iconFill?: string; - /** Icon Width */ - iconWidth?: number; + /** Secondary icon to display on the left side of component, right of the icon */ + secondaryIcon?: IconAsset; - /** Icon Height */ - iconHeight?: number; + /** The fill color to pass into the secondary icon. */ + secondaryIconFill?: string; - /** Any additional styles to pass to the icon container. */ - iconStyles?: StyleProp; + /** Icon Width */ + iconWidth?: number; - /** A fallback avatar icon to display when there is an error on loading avatar from remote URL. */ - fallbackIcon?: IconAsset; + /** Icon Height */ + iconHeight?: number; - /** An icon to display under the main item */ - furtherDetailsIcon?: IconAsset; + /** Any additional styles to pass to the icon container. */ + iconStyles?: StyleProp; - /** Boolean whether to display the title right icon */ - shouldShowTitleIcon?: boolean; + /** A fallback avatar icon to display when there is an error on loading avatar from remote URL. */ + fallbackIcon?: IconAsset; - /** Icon to display at right side of title */ - titleIcon?: IconAsset; + /** An icon to display under the main item */ + furtherDetailsIcon?: IconAsset; - /** Boolean whether to display the right icon */ - shouldShowRightIcon?: boolean; + /** Boolean whether to display the title right icon */ + shouldShowTitleIcon?: boolean; - /** Overrides the icon for shouldShowRightIcon */ - iconRight?: IconAsset; + /** Icon to display at right side of title */ + titleIcon?: IconAsset; - /** Should render component on the right */ - shouldShowRightComponent?: boolean; + /** Boolean whether to display the right icon */ + shouldShowRightIcon?: boolean; - /** Component to be displayed on the right */ - rightComponent?: ReactNode; + /** Overrides the icon for shouldShowRightIcon */ + iconRight?: IconAsset; - /** A description text to show under the title */ - description?: string; + /** Should render component on the right */ + shouldShowRightComponent?: boolean; - /** Should the description be shown above the title (instead of the other way around) */ - shouldShowDescriptionOnTop?: boolean; + /** Component to be displayed on the right */ + rightComponent?: ReactNode; - /** Error to display below the title */ - error?: string; + /** A description text to show under the title */ + description?: string; - /** Error to display at the bottom of the component */ - errorText?: string; + /** Should the description be shown above the title (instead of the other way around) */ + shouldShowDescriptionOnTop?: boolean; - /** A boolean flag that gives the icon a green fill if true */ - success?: boolean; + /** Error to display below the title */ + error?: string; - /** Whether item is focused or active */ - focused?: boolean; + /** Error to display at the bottom of the component */ + errorText?: string; - /** Should we disable this menu item? */ - disabled?: boolean; + /** A boolean flag that gives the icon a green fill if true */ + success?: boolean; - /** Text that appears above the title */ - label?: string; + /** Whether item is focused or active */ + focused?: boolean; - /** Label to be displayed on the right */ - rightLabel?: string; + /** Should we disable this menu item? */ + disabled?: boolean; - /** Text to display for the item */ - title?: string; + /** Text that appears above the title */ + label?: string; - /** A right-aligned subtitle for this menu option */ - subtitle?: string | number; + /** Label to be displayed on the right */ + rightLabel?: string; - /** Should the title show with normal font weight (not bold) */ - shouldShowBasicTitle?: boolean; + /** Text to display for the item */ + title?: string; - /** Should we make this selectable with a checkbox */ - shouldShowSelectedState?: boolean; + /** A right-aligned subtitle for this menu option */ + subtitle?: string | number; - /** Whether this item is selected */ - isSelected?: boolean; + /** Should the title show with normal font weight (not bold) */ + shouldShowBasicTitle?: boolean; - /** Prop to identify if we should load avatars vertically instead of diagonally */ - shouldStackHorizontally: boolean; + /** Should we make this selectable with a checkbox */ + shouldShowSelectedState?: boolean; - /** Prop to represent the size of the avatar images to be shown */ - avatarSize?: (typeof CONST.AVATAR_SIZE)[keyof typeof CONST.AVATAR_SIZE]; + /** Whether this item is selected */ + isSelected?: boolean; - /** Avatars to show on the right of the menu item */ - floatRightAvatars?: IconType[]; + /** Prop to identify if we should load avatars vertically instead of diagonally */ + shouldStackHorizontally?: boolean; - /** Prop to represent the size of the float right avatar images to be shown */ - floatRightAvatarSize?: ValueOf; + /** Prop to represent the size of the avatar images to be shown */ + avatarSize?: (typeof CONST.AVATAR_SIZE)[keyof typeof CONST.AVATAR_SIZE]; - /** Affects avatar size */ - viewMode?: ValueOf; + /** Avatars to show on the right of the menu item */ + floatRightAvatars?: IconType[]; - /** Used to truncate the text with an ellipsis after computing the text layout */ - numberOfLinesTitle?: number; + /** Prop to represent the size of the float right avatar images to be shown */ + floatRightAvatarSize?: ValueOf; - /** Whether we should use small avatar subscript sizing the for menu item */ - isSmallAvatarSubscriptMenu?: boolean; + /** Affects avatar size */ + viewMode?: ValueOf; - /** The type of brick road indicator to show. */ - brickRoadIndicator?: ValueOf; + /** Used to truncate the text with an ellipsis after computing the text layout */ + numberOfLinesTitle?: number; - /** Should render the content in HTML format */ - shouldRenderAsHTML?: boolean; + /** Whether we should use small avatar subscript sizing the for menu item */ + isSmallAvatarSubscriptMenu?: boolean; - /** Should we grey out the menu item when it is disabled? */ - shouldGreyOutWhenDisabled?: boolean; + /** The type of brick road indicator to show. */ + brickRoadIndicator?: ValueOf; - /** The action accept for anonymous user or not */ - isAnonymousAction?: boolean; + /** Should render the content in HTML format */ + shouldRenderAsHTML?: boolean; - /** Flag to indicate whether or not text selection should be disabled from long-pressing the menu item. */ - shouldBlockSelection?: boolean; + /** Should we grey out the menu item when it is disabled? */ + shouldGreyOutWhenDisabled?: boolean; - /** Whether should render title as HTML or as Text */ - shouldParseTitle?: false; + /** The action accept for anonymous user or not */ + isAnonymousAction?: boolean; - /** Should check anonymous user in onPress function */ - shouldCheckActionAllowedOnPress?: boolean; + /** Flag to indicate whether or not text selection should be disabled from long-pressing the menu item. */ + shouldBlockSelection?: boolean; - /** Text to display under the main item */ - furtherDetails?: string; + /** Whether should render title as HTML or as Text */ + shouldParseTitle?: false; - /** The function that should be called when this component is LongPressed or right-clicked. */ - onSecondaryInteraction: () => void; + /** Should check anonymous user in onPress function */ + shouldCheckActionAllowedOnPress?: boolean; - /** Array of objects that map display names to their corresponding tooltip */ - titleWithTooltips: DisplayNameWithTooltip[]; + /** Text to display under the main item */ + furtherDetails?: string; - /** Icon should be displayed in its own color */ - displayInDefaultIconColor?: boolean; + /** The function that should be called when this component is LongPressed or right-clicked. */ + onSecondaryInteraction?: () => void; - /** Determines how the icon should be resized to fit its container */ - contentFit?: ImageContentFit; - }; + /** Array of objects that map display names to their corresponding tooltip */ + titleWithTooltips?: DisplayNameWithTooltip[]; + + /** Icon should be displayed in its own color */ + displayInDefaultIconColor?: boolean; + + /** Determines how the icon should be resized to fit its container */ + contentFit?: ImageContentFit; +}; function MenuItem( { @@ -298,7 +289,7 @@ function MenuItem( const theme = useTheme(); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); - const combinedStyle = StyleUtils.combineStyles(style ?? {}, styles.popoverMenuItem); + const combinedStyle = [style, styles.popoverMenuItem]; const {isSmallScreenWidth} = useWindowDimensions(); const [html, setHtml] = useState(''); const titleRef = useRef(''); diff --git a/src/components/VideoChatButtonAndMenu/BaseVideoChatButtonAndMenu.tsx b/src/components/VideoChatButtonAndMenu/BaseVideoChatButtonAndMenu.tsx index 906f9530af9f..c180fa670efc 100755 --- a/src/components/VideoChatButtonAndMenu/BaseVideoChatButtonAndMenu.tsx +++ b/src/components/VideoChatButtonAndMenu/BaseVideoChatButtonAndMenu.tsx @@ -29,8 +29,8 @@ function BaseVideoChatButtonAndMenu(props: BaseVideoChatButtonAndMenuProps) { const {isSmallScreenWidth} = useWindowDimensions(); const [isVideoChatMenuActive, setIsVideoChatMenuActive] = useState(false); const [videoChatIconPosition, setVideoChatIconPosition] = useState({x: 0, y: 0}); - const videoChatIconWrapperRef = useRef(null); - const videoChatButtonRef = useRef(null); + const videoChatIconWrapperRef = useRef(null); + const videoChatButtonRef = useRef(null); const menuItemData = [ { @@ -124,6 +124,7 @@ function BaseVideoChatButtonAndMenu(props: BaseVideoChatButtonAndMenuProps) { wrapperStyle={styles.mr3} key={text} icon={icon} + iconType={CONST.ICON_TYPE_ICON} title={text} onPress={onPress} /> From 0ab996ceb60081b96f464c3597c10cfe79693d72 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 2 Jan 2024 17:22:05 +0100 Subject: [PATCH 3/8] Fix ref types --- src/components/Modal/types.ts | 4 +- src/components/Popover/types.ts | 42 ++++++++++--------- src/components/PopoverProvider/types.ts | 8 ++-- .../PopoverWithoutOverlay/index.tsx | 3 +- src/components/PopoverWithoutOverlay/types.ts | 4 +- 5 files changed, 33 insertions(+), 28 deletions(-) diff --git a/src/components/Modal/types.ts b/src/components/Modal/types.ts index 461a5935eda9..af6d90268521 100644 --- a/src/components/Modal/types.ts +++ b/src/components/Modal/types.ts @@ -1,4 +1,4 @@ -import {ViewStyle} from 'react-native'; +import {View, ViewStyle} from 'react-native'; import {ModalProps} from 'react-native-modal'; import {ValueOf} from 'type-fest'; import {WindowDimensionsProps} from '@components/withWindowDimensions/types'; @@ -23,7 +23,7 @@ type BaseModalProps = WindowDimensionsProps & shouldSetModalVisibility?: boolean; /** Callback method fired when the user requests to close the modal */ - onClose: (ref?: React.RefObject) => void; + onClose: (ref?: React.RefObject) => void; /** State that determines whether to display the modal or not */ isVisible: boolean; diff --git a/src/components/Popover/types.ts b/src/components/Popover/types.ts index 103ab0404081..f58f282c599d 100644 --- a/src/components/Popover/types.ts +++ b/src/components/Popover/types.ts @@ -1,38 +1,42 @@ -import {ReactNode, RefObject} from 'react'; +import {RefObject} from 'react'; import {View} from 'react-native'; import BaseModalProps, {PopoverAnchorPosition} from '@components/Modal/types'; import {WindowDimensionsProps} from '@components/withWindowDimensions/types'; +import ChildrenProps from '@src/types/utils/ChildrenProps'; + +type AnchorAlignment = {horizontal: string; vertical: string}; type PopoverDimensions = { width: number; height: number; }; -type PopoverProps = BaseModalProps & { - /** The anchor position of the popover */ - anchorPosition?: PopoverAnchorPosition; +type PopoverProps = BaseModalProps & + ChildrenProps & { + /** The anchor position of the popover */ + anchorPosition?: PopoverAnchorPosition; - /** The anchor ref of the popover */ - anchorRef: RefObject; + /** The anchor alignment of the popover */ + anchorAlignment?: AnchorAlignment; - /** Whether disable the animations */ - disableAnimation?: boolean; + /** The anchor ref of the popover */ + anchorRef: RefObject; - /** Whether we don't want to show overlay */ - withoutOverlay: boolean; + /** Whether disable the animations */ + disableAnimation?: boolean; - /** The dimensions of the popover */ - popoverDimensions?: PopoverDimensions; + /** Whether we don't want to show overlay */ + withoutOverlay: boolean; - /** The ref of the popover */ - withoutOverlayRef?: RefObject; + /** The dimensions of the popover */ + popoverDimensions?: PopoverDimensions; - /** Whether we want to show the popover on the right side of the screen */ - fromSidebarMediumScreen?: boolean; + /** The ref of the popover */ + withoutOverlayRef?: RefObject; - /** The popover children */ - children: ReactNode; -}; + /** Whether we want to show the popover on the right side of the screen */ + fromSidebarMediumScreen?: boolean; + }; type PopoverWithWindowDimensionsProps = PopoverProps & WindowDimensionsProps; diff --git a/src/components/PopoverProvider/types.ts b/src/components/PopoverProvider/types.ts index dc0208e10dd7..a638ed982196 100644 --- a/src/components/PopoverProvider/types.ts +++ b/src/components/PopoverProvider/types.ts @@ -8,14 +8,14 @@ type PopoverContextProps = { type PopoverContextValue = { onOpen?: (popoverParams: AnchorRef) => void; popover?: AnchorRef | Record | null; - close: (anchorRef?: RefObject) => void; + close: (anchorRef?: RefObject) => void; isOpen: boolean; }; type AnchorRef = { - ref: RefObject; - close: (anchorRef?: RefObject) => void; - anchorRef: RefObject; + ref: RefObject; + close: (anchorRef?: RefObject) => void; + anchorRef: RefObject; onOpenCallback?: () => void; onCloseCallback?: () => void; }; diff --git a/src/components/PopoverWithoutOverlay/index.tsx b/src/components/PopoverWithoutOverlay/index.tsx index f83949bcbe9d..0d4ef7d2e912 100644 --- a/src/components/PopoverWithoutOverlay/index.tsx +++ b/src/components/PopoverWithoutOverlay/index.tsx @@ -7,6 +7,7 @@ import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import * as Modal from '@userActions/Modal'; +import viewRef from '@src/types/utils/viewRef'; import PopoverWithoutOverlayProps from './types'; function PopoverWithoutOverlay( @@ -118,7 +119,7 @@ function PopoverWithoutOverlay( return ( ; + anchorRef: React.RefObject; /** A react-native-animatable animation timing for the modal display animation */ animationInTiming?: number; @@ -22,7 +22,7 @@ type PopoverWithoutOverlayProps = ChildrenProps & disableAnimation?: boolean; /** The ref of the popover */ - withoutOverlayRef: React.RefObject; + withoutOverlayRef: React.RefObject; }; export default PopoverWithoutOverlayProps; From 888d74d8b797be0b4721a38e7e2a578420fb8afb Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 3 Jan 2024 12:59:39 +0100 Subject: [PATCH 4/8] Import RefObject --- src/components/PopoverWithoutOverlay/types.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/PopoverWithoutOverlay/types.ts b/src/components/PopoverWithoutOverlay/types.ts index 04e9a9cb7d70..06df0d00086d 100644 --- a/src/components/PopoverWithoutOverlay/types.ts +++ b/src/components/PopoverWithoutOverlay/types.ts @@ -1,3 +1,4 @@ +import {RefObject} from 'react'; import {View} from 'react-native'; import BaseModalProps from '@components/Modal/types'; import ChildrenProps from '@src/types/utils/ChildrenProps'; @@ -13,7 +14,7 @@ type PopoverWithoutOverlayProps = ChildrenProps & }; /** The anchor ref of the popover */ - anchorRef: React.RefObject; + anchorRef: RefObject; /** A react-native-animatable animation timing for the modal display animation */ animationInTiming?: number; @@ -22,7 +23,7 @@ type PopoverWithoutOverlayProps = ChildrenProps & disableAnimation?: boolean; /** The ref of the popover */ - withoutOverlayRef: React.RefObject; + withoutOverlayRef: RefObject; }; export default PopoverWithoutOverlayProps; From 5c40d4db4ac7b869a0964345adef17fdaefc04d6 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 4 Jan 2024 10:16:34 +0100 Subject: [PATCH 5/8] Fix type imports --- src/components/PopoverProvider/types.ts | 4 ++-- .../VideoChatButtonAndMenu/BaseVideoChatButtonAndMenu.tsx | 2 +- src/components/VideoChatButtonAndMenu/index.android.tsx | 2 +- src/components/VideoChatButtonAndMenu/index.tsx | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/PopoverProvider/types.ts b/src/components/PopoverProvider/types.ts index a638ed982196..49705d7ea7a8 100644 --- a/src/components/PopoverProvider/types.ts +++ b/src/components/PopoverProvider/types.ts @@ -1,5 +1,5 @@ -import {ReactNode, RefObject} from 'react'; -import {View} from 'react-native'; +import type {ReactNode, RefObject} from 'react'; +import type {View} from 'react-native'; type PopoverContextProps = { children: ReactNode; diff --git a/src/components/VideoChatButtonAndMenu/BaseVideoChatButtonAndMenu.tsx b/src/components/VideoChatButtonAndMenu/BaseVideoChatButtonAndMenu.tsx index c180fa670efc..6c591dd99434 100755 --- a/src/components/VideoChatButtonAndMenu/BaseVideoChatButtonAndMenu.tsx +++ b/src/components/VideoChatButtonAndMenu/BaseVideoChatButtonAndMenu.tsx @@ -15,7 +15,7 @@ import useWindowDimensions from '@hooks/useWindowDimensions'; import * as Link from '@userActions/Link'; import * as Session from '@userActions/Session'; import CONST from '@src/CONST'; -import VideoChatButtonAndMenuProps from './types'; +import type VideoChatButtonAndMenuProps from './types'; type BaseVideoChatButtonAndMenuProps = VideoChatButtonAndMenuProps & { /** Link to open when user wants to create a new google meet meeting */ diff --git a/src/components/VideoChatButtonAndMenu/index.android.tsx b/src/components/VideoChatButtonAndMenu/index.android.tsx index f7b46ee627e9..838d296074fa 100644 --- a/src/components/VideoChatButtonAndMenu/index.android.tsx +++ b/src/components/VideoChatButtonAndMenu/index.android.tsx @@ -1,7 +1,7 @@ import React from 'react'; import CONST from '@src/CONST'; import BaseVideoChatButtonAndMenu from './BaseVideoChatButtonAndMenu'; -import VideoChatButtonAndMenuProps from './types'; +import type VideoChatButtonAndMenuProps from './types'; // On Android creating a new google meet meeting requires the CALL_PHONE permission in some cases // so we're just opening the google meet app instead, more details: diff --git a/src/components/VideoChatButtonAndMenu/index.tsx b/src/components/VideoChatButtonAndMenu/index.tsx index 977f47b8a349..fa381c18d64a 100644 --- a/src/components/VideoChatButtonAndMenu/index.tsx +++ b/src/components/VideoChatButtonAndMenu/index.tsx @@ -1,7 +1,7 @@ import React from 'react'; import CONST from '@src/CONST'; import BaseVideoChatButtonAndMenu from './BaseVideoChatButtonAndMenu'; -import VideoChatButtonAndMenuProps from './types'; +import type VideoChatButtonAndMenuProps from './types'; function VideoChatButtonAndMenu(props: VideoChatButtonAndMenuProps) { return ( From 37f34aa18d21f10794e75ac80b1b6c6007602514 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 9 Jan 2024 15:22:36 +0100 Subject: [PATCH 6/8] Fix anchor type --- src/components/ProcessMoneyRequestHoldMenu.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/ProcessMoneyRequestHoldMenu.tsx b/src/components/ProcessMoneyRequestHoldMenu.tsx index 1b711633ed3b..dbd0648694ae 100644 --- a/src/components/ProcessMoneyRequestHoldMenu.tsx +++ b/src/components/ProcessMoneyRequestHoldMenu.tsx @@ -1,3 +1,4 @@ +import type {RefObject} from 'react'; import React from 'react'; import {View} from 'react-native'; import useLocalize from '@hooks/useLocalize'; @@ -27,7 +28,7 @@ type ProcessMoneyRequestHoldMenuProps = { anchorAlignment: AnchorAlignment; /** The anchor ref of the popover menu */ - anchorRef: React.RefObject; + anchorRef: RefObject; }; function ProcessMoneyRequestHoldMenu({isVisible, onClose, onConfirm, anchorPosition, anchorAlignment, anchorRef}: ProcessMoneyRequestHoldMenuProps) { From eed32cca96713df291685456798564052f809437 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Tue, 9 Jan 2024 15:29:51 +0100 Subject: [PATCH 7/8] Fix anchor type x2 --- src/components/ProcessMoneyRequestHoldMenu.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ProcessMoneyRequestHoldMenu.tsx b/src/components/ProcessMoneyRequestHoldMenu.tsx index dbd0648694ae..5f32240aca9b 100644 --- a/src/components/ProcessMoneyRequestHoldMenu.tsx +++ b/src/components/ProcessMoneyRequestHoldMenu.tsx @@ -28,7 +28,7 @@ type ProcessMoneyRequestHoldMenuProps = { anchorAlignment: AnchorAlignment; /** The anchor ref of the popover menu */ - anchorRef: RefObject; + anchorRef: RefObject; }; function ProcessMoneyRequestHoldMenu({isVisible, onClose, onConfirm, anchorPosition, anchorAlignment, anchorRef}: ProcessMoneyRequestHoldMenuProps) { From 0ed4a730f399906f0ef7c168e34272f4a28826b9 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Wed, 10 Jan 2024 13:41:41 +0100 Subject: [PATCH 8/8] Add missing default values --- .../BaseVideoChatButtonAndMenu.tsx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/components/VideoChatButtonAndMenu/BaseVideoChatButtonAndMenu.tsx b/src/components/VideoChatButtonAndMenu/BaseVideoChatButtonAndMenu.tsx index 6c591dd99434..9f615cef525d 100755 --- a/src/components/VideoChatButtonAndMenu/BaseVideoChatButtonAndMenu.tsx +++ b/src/components/VideoChatButtonAndMenu/BaseVideoChatButtonAndMenu.tsx @@ -22,7 +22,7 @@ type BaseVideoChatButtonAndMenuProps = VideoChatButtonAndMenuProps & { googleMeetURL: string; }; -function BaseVideoChatButtonAndMenu(props: BaseVideoChatButtonAndMenuProps) { +function BaseVideoChatButtonAndMenu({googleMeetURL, isConcierge = false, guideCalendarLink}: BaseVideoChatButtonAndMenuProps) { const theme = useTheme(); const styles = useThemeStyles(); const {translate} = useLocalize(); @@ -46,7 +46,7 @@ function BaseVideoChatButtonAndMenu(props: BaseVideoChatButtonAndMenuProps) { text: translate('videoChatButtonAndMenu.googleMeet'), onPress: () => { setIsVideoChatMenuActive(false); - Link.openExternalLink(props.googleMeetURL); + Link.openExternalLink(googleMeetURL); }, }, ]; @@ -90,8 +90,8 @@ function BaseVideoChatButtonAndMenu(props: BaseVideoChatButtonAndMenuProps) { videoChatButtonRef.current?.blur(); // If this is the Concierge chat, we'll open the modal for requesting a setup call instead - if (props.isConcierge && props.guideCalendarLink) { - Link.openExternalLink(props.guideCalendarLink); + if (isConcierge && guideCalendarLink) { + Link.openExternalLink(guideCalendarLink); return; } setIsVideoChatMenuActive((previousVal) => !previousVal); @@ -124,7 +124,6 @@ function BaseVideoChatButtonAndMenu(props: BaseVideoChatButtonAndMenuProps) { wrapperStyle={styles.mr3} key={text} icon={icon} - iconType={CONST.ICON_TYPE_ICON} title={text} onPress={onPress} />