From d75347fba52cd568e7d61af2d4a8fc9bf38e6a11 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Mon, 20 Nov 2023 11:07:24 +0100 Subject: [PATCH 01/26] migrate Tooltip to TS --- src/components/Hoverable/types.ts | 4 +- src/components/Tooltip/BaseTooltip.native.js | 21 ---- src/components/Tooltip/BaseTooltip.native.tsx | 14 +++ .../{BaseTooltip.js => BaseTooltip.tsx} | 25 ++-- ...horTooltip.js => PopoverAnchorTooltip.tsx} | 31 ++--- ...eBody.js => TooltipRenderedOnPageBody.tsx} | 119 +++++++++--------- .../{TooltipSense.js => TooltipSense.tsx} | 4 +- src/components/Tooltip/index.js | 37 ------ src/components/Tooltip/index.tsx | 26 ++++ .../Tooltip/{tooltipPropTypes.js => types.ts} | 41 ++---- 10 files changed, 134 insertions(+), 188 deletions(-) delete mode 100644 src/components/Tooltip/BaseTooltip.native.js create mode 100644 src/components/Tooltip/BaseTooltip.native.tsx rename src/components/Tooltip/{BaseTooltip.js => BaseTooltip.tsx} (90%) rename src/components/Tooltip/{PopoverAnchorTooltip.js => PopoverAnchorTooltip.tsx} (56%) rename src/components/Tooltip/{TooltipRenderedOnPageBody.js => TooltipRenderedOnPageBody.tsx} (60%) rename src/components/Tooltip/{TooltipSense.js => TooltipSense.tsx} (82%) delete mode 100644 src/components/Tooltip/index.js create mode 100644 src/components/Tooltip/index.tsx rename src/components/Tooltip/{tooltipPropTypes.js => types.ts} (50%) diff --git a/src/components/Hoverable/types.ts b/src/components/Hoverable/types.ts index 430b865f50c5..aa38e0435b07 100644 --- a/src/components/Hoverable/types.ts +++ b/src/components/Hoverable/types.ts @@ -1,8 +1,8 @@ -import {ReactElement} from 'react'; +import {ReactElement, ReactNode} from 'react'; type HoverableProps = { /** Children to wrap with Hoverable. */ - children: ((isHovered: boolean) => ReactElement) | ReactElement; + children: ((isHovered: boolean) => ReactElement) | ReactNode; /** Whether to disable the hover action */ disabled?: boolean; diff --git a/src/components/Tooltip/BaseTooltip.native.js b/src/components/Tooltip/BaseTooltip.native.js deleted file mode 100644 index fb2e3eba41c0..000000000000 --- a/src/components/Tooltip/BaseTooltip.native.js +++ /dev/null @@ -1,21 +0,0 @@ -import PropTypes from 'prop-types'; - -// We can't use the common component for the Tooltip as Web implementation uses DOM specific method to -// render the View which is not present on the Mobile. -const propTypes = { - /** Children to wrap with Tooltip. */ - children: PropTypes.node.isRequired, -}; - -/** - * @param {propTypes} props - * @returns {ReactNodeLike} - */ -function Tooltip(props) { - return props.children; -} - -Tooltip.propTypes = propTypes; -Tooltip.displayName = 'Tooltip'; - -export default Tooltip; diff --git a/src/components/Tooltip/BaseTooltip.native.tsx b/src/components/Tooltip/BaseTooltip.native.tsx new file mode 100644 index 000000000000..7f54f5cb9668 --- /dev/null +++ b/src/components/Tooltip/BaseTooltip.native.tsx @@ -0,0 +1,14 @@ +// We can't use the common component for the Tooltip as Web implementation uses DOM specific method to +import {ReactNode} from 'react'; + +// render the View which is not present on the Mobile. +type TooltipProps = { + children: ReactNode; +}; +function Tooltip({children}: TooltipProps) { + return children; +} + +Tooltip.displayName = 'Tooltip'; + +export default Tooltip; diff --git a/src/components/Tooltip/BaseTooltip.js b/src/components/Tooltip/BaseTooltip.tsx similarity index 90% rename from src/components/Tooltip/BaseTooltip.js rename to src/components/Tooltip/BaseTooltip.tsx index 3eb905e7a3e5..bf99216bd827 100644 --- a/src/components/Tooltip/BaseTooltip.js +++ b/src/components/Tooltip/BaseTooltip.tsx @@ -1,16 +1,16 @@ import {BoundsObserver} from '@react-ng/bounds-observer'; -import Str from 'expensify-common/lib/str'; +import _ from 'lodash'; import React, {memo, useCallback, useEffect, useRef, useState} from 'react'; import {Animated} from 'react-native'; -import _ from 'underscore'; import Hoverable from '@components/Hoverable'; import useLocalize from '@hooks/useLocalize'; import usePrevious from '@hooks/usePrevious'; import useWindowDimensions from '@hooks/useWindowDimensions'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; -import * as tooltipPropTypes from './tooltipPropTypes'; +import variables from '@styles/variables'; import TooltipRenderedOnPageBody from './TooltipRenderedOnPageBody'; import TooltipSense from './TooltipSense'; +import {TooltipProps} from './types'; const hasHoverSupport = DeviceCapabilities.hasHoverSupport(); @@ -33,7 +33,7 @@ const hasHoverSupport = DeviceCapabilities.hasHoverSupport(); * @return {DOMRect} The chosen bounding box. */ -function chooseBoundingBox(target, clientX, clientY) { +function chooseBoundingBox(target: HTMLElement, clientX: number, clientY: number): DOMRect { const slop = 5; const bbs = target.getClientRects(); const clientXMin = clientX - slop; @@ -41,6 +41,7 @@ function chooseBoundingBox(target, clientX, clientY) { const clientYMin = clientY - slop; const clientYMax = clientY + slop; + // eslint-disable-next-line @typescript-eslint/prefer-for-of for (let i = 0; i < bbs.length; i++) { const bb = bbs[i]; if (clientXMin <= bb.right && clientXMax >= bb.left && clientYMin <= bb.bottom && clientYMax >= bb.top) { @@ -52,7 +53,7 @@ function chooseBoundingBox(target, clientX, clientY) { return target.getBoundingClientRect(); } -function Tooltip({children, numberOfLines, maxWidth, text, renderTooltipContent, renderTooltipContentKey, shouldHandleScroll, shiftHorizontal, shiftVertical, tooltipRef}) { +function Tooltip({children, numberOfLines, maxWidth = variables.sideBarWidth, text = '', renderTooltipContent, renderTooltipContentKey, shouldHandleScroll, shiftHorizontal = 0, shiftVertical = 0, tooltipRef}: TooltipProps) { const {preferredLocale} = useLocalize(); const {windowWidth} = useWindowDimensions(); @@ -74,10 +75,10 @@ function Tooltip({children, numberOfLines, maxWidth, text, renderTooltipContent, const isAnimationCanceled = useRef(false); const prevText = usePrevious(text); - const target = useRef(null); + const target = useRef(null); const initialMousePosition = useRef({x: 0, y: 0}); - const updateTargetAndMousePosition = useCallback((e) => { + const updateTargetAndMousePosition = useCallback((e: MouseEvent) => { target.current = e.currentTarget; initialMousePosition.current = {x: e.clientX, y: e.clientY}; }, []); @@ -120,10 +121,8 @@ function Tooltip({children, numberOfLines, maxWidth, text, renderTooltipContent, /** * Update the tooltip bounding rectangle - * - * @param {Object} bounds - updated bounds */ - const updateBounds = (bounds) => { + const updateBounds = (bounds: DOMRect) => { if (bounds.width === 0) { setIsRendered(false); } @@ -183,8 +182,8 @@ function Tooltip({children, numberOfLines, maxWidth, text, renderTooltipContent, yOffset={yOffset} targetWidth={wrapperWidth} targetHeight={wrapperHeight} - shiftHorizontal={Str.result(shiftHorizontal)} - shiftVertical={Str.result(shiftVertical)} + shiftHorizontal={shiftHorizontal} + shiftVertical={shiftVertical} text={text} maxWidth={maxWidth} numberOfLines={numberOfLines} @@ -212,8 +211,6 @@ function Tooltip({children, numberOfLines, maxWidth, text, renderTooltipContent, ); } -Tooltip.propTypes = tooltipPropTypes.propTypes; -Tooltip.defaultProps = tooltipPropTypes.defaultProps; Tooltip.displayName = 'Tooltip'; export default memo(Tooltip); diff --git a/src/components/Tooltip/PopoverAnchorTooltip.js b/src/components/Tooltip/PopoverAnchorTooltip.tsx similarity index 56% rename from src/components/Tooltip/PopoverAnchorTooltip.js rename to src/components/Tooltip/PopoverAnchorTooltip.tsx index 6123be56544c..748cf5d9e229 100644 --- a/src/components/Tooltip/PopoverAnchorTooltip.js +++ b/src/components/Tooltip/PopoverAnchorTooltip.tsx @@ -1,36 +1,21 @@ -import PropTypes from 'prop-types'; import React, {useContext, useMemo, useRef} from 'react'; +import {View} from 'react-native'; import {PopoverContext} from '@components/PopoverProvider'; import BaseTooltip from './BaseTooltip'; -import {defaultProps as tooltipDefaultProps, propTypes as tooltipPropTypes} from './tooltipPropTypes'; - -const propTypes = { - ...tooltipPropTypes, +import {TooltipProps} from './types'; +type Props = TooltipProps & { /** Whether the actual Tooltip should be rendered. If false, it's just going to return the children */ - shouldRender: PropTypes.bool, -}; - -const defaultProps = { - ...tooltipDefaultProps, - shouldRender: true, + shouldRender: boolean; }; - -function PopoverAnchorTooltip({shouldRender, children, ...props}) { +function PopoverAnchorTooltip({shouldRender = true, children, ...props}: Props) { const {isOpen, popover} = useContext(PopoverContext); - const tooltipRef = useRef(null); + const tooltipRef = useRef(null); const isPopoverRelatedToTooltipOpen = useMemo(() => { // eslint-disable-next-line const tooltipNode = tooltipRef.current ? tooltipRef.current._childNode : null; - if ( - isOpen && - popover && - popover.anchorRef && - popover.anchorRef.current && - tooltipNode && - (tooltipNode.contains(popover.anchorRef.current) || tooltipNode === popover.anchorRef.current) - ) { + if (isOpen && popover?.anchorRef?.current && tooltipNode && (tooltipNode.contains(popover.anchorRef.current) || tooltipNode === popover.anchorRef.current)) { return true; } @@ -53,7 +38,5 @@ function PopoverAnchorTooltip({shouldRender, children, ...props}) { } PopoverAnchorTooltip.displayName = 'PopoverAnchorTooltip'; -PopoverAnchorTooltip.propTypes = propTypes; -PopoverAnchorTooltip.defaultProps = defaultProps; export default PopoverAnchorTooltip; diff --git a/src/components/Tooltip/TooltipRenderedOnPageBody.js b/src/components/Tooltip/TooltipRenderedOnPageBody.tsx similarity index 60% rename from src/components/Tooltip/TooltipRenderedOnPageBody.js rename to src/components/Tooltip/TooltipRenderedOnPageBody.tsx index 69d06775ae2a..64cd5c126c7b 100644 --- a/src/components/Tooltip/TooltipRenderedOnPageBody.js +++ b/src/components/Tooltip/TooltipRenderedOnPageBody.tsx @@ -1,4 +1,3 @@ -import PropTypes from 'prop-types'; import React, {useEffect, useLayoutEffect, useMemo, useRef, useState} from 'react'; import ReactDOM from 'react-dom'; import {Animated, View} from 'react-native'; @@ -6,136 +5,138 @@ import Text from '@components/Text'; import Log from '@libs/Log'; import getTooltipStyles from '@styles/getTooltipStyles'; -const propTypes = { +type TooltipRenderedOnPageBodyProps = { /** Window width */ - windowWidth: PropTypes.number.isRequired, + windowWidth: number; /** Tooltip Animation value */ // eslint-disable-next-line react/forbid-prop-types - animation: PropTypes.object.isRequired, + animation: unknown; /** The distance between the left side of the wrapper view and the left side of the window */ - xOffset: PropTypes.number.isRequired, + xOffset: number; /** The distance between the top of the wrapper view and the top of the window */ - yOffset: PropTypes.number.isRequired, + yOffset: number; /** The width of the tooltip's target */ - targetWidth: PropTypes.number.isRequired, + targetWidth: number; /** The height of the tooltip's target */ - targetHeight: PropTypes.number.isRequired, + targetHeight: number; /** Any additional amount to manually adjust the horizontal position of the tooltip. A positive value shifts the tooltip to the right, and a negative value shifts it to the left. */ - shiftHorizontal: PropTypes.number, + shiftHorizontal?: number; /** Any additional amount to manually adjust the vertical position of the tooltip. A positive value shifts the tooltip down, and a negative value shifts it up. */ - shiftVertical: PropTypes.number, + shiftVertical?: number; /** Text to be shown in the tooltip */ - text: PropTypes.string.isRequired, + text: string; /** Maximum number of lines to show in tooltip */ - numberOfLines: PropTypes.number.isRequired, + numberOfLines: number; /** Number of pixels to set max-width on tooltip */ - maxWidth: PropTypes.number, + maxWidth?: number; /** Render custom content inside the tooltip. Note: This cannot be used together with the text props. */ - renderTooltipContent: PropTypes.func, + renderTooltipContent?: () => React.ReactNode; }; -const defaultProps = { - shiftHorizontal: 0, - shiftVertical: 0, - renderTooltipContent: undefined, - maxWidth: 0, -}; +const viewRef = (ref: React.RefObject) => ref as React.RefObject; // Props will change frequently. // On every tooltip hover, we update the position in state which will result in re-rendering. // We also update the state on layout changes which will be triggered often. // There will be n number of tooltip components in the page. // It's good to memoize this one. -function TooltipRenderedOnPageBody(props) { +function TooltipRenderedOnPageBody({ + windowWidth, + animation, + xOffset, + yOffset, + targetWidth, + targetHeight, + shiftHorizontal = 0, + shiftVertical = 0, + text, + numberOfLines, + maxWidth = 0, + renderTooltipContent, +}: TooltipRenderedOnPageBodyProps) { // The width of tooltip's inner content. Has to be undefined in the beginning // as a width of 0 will cause the content to be rendered of a width of 0, // which prevents us from measuring it correctly. - const [contentMeasuredWidth, setContentMeasuredWidth] = useState(undefined); + const [contentMeasuredWidth, setContentMeasuredWidth] = useState(undefined); // The height of tooltip's wrapper. - const [wrapperMeasuredHeight, setWrapperMeasuredHeight] = useState(undefined); - const contentRef = useRef(); - const rootWrapper = useRef(); + const [wrapperMeasuredHeight, setWrapperMeasuredHeight] = useState(undefined); + const contentRef = useRef(null); + const rootWrapper = useRef(null); useEffect(() => { - if (!props.renderTooltipContent || !props.text) { + if (!renderTooltipContent || !text) { return; } Log.warn('Developer error: Cannot use both text and renderTooltipContent props at the same time in !'); - }, [props.text, props.renderTooltipContent]); + }, [text, renderTooltipContent]); useLayoutEffect(() => { // Calculate the tooltip width and height before the browser repaints the screen to prevent flicker // because of the late update of the width and the height from onLayout. - setContentMeasuredWidth(contentRef.current.getBoundingClientRect().width); - setWrapperMeasuredHeight(rootWrapper.current.getBoundingClientRect().height); + setContentMeasuredWidth(contentRef.current?.getBoundingClientRect().width); + setWrapperMeasuredHeight(rootWrapper.current?.getBoundingClientRect().height); }, []); const {animationStyle, rootWrapperStyle, textStyle, pointerWrapperStyle, pointerStyle} = useMemo( () => getTooltipStyles( rootWrapper.current, - props.animation, - props.windowWidth, - props.xOffset, - props.yOffset, - props.targetWidth, - props.targetHeight, - props.maxWidth, + animation, + windowWidth, + xOffset, + yOffset, + targetWidth, + targetHeight, + maxWidth, contentMeasuredWidth, wrapperMeasuredHeight, - props.shiftHorizontal, - props.shiftVertical, + shiftHorizontal, + shiftVertical, ), - [ - props.animation, - props.windowWidth, - props.xOffset, - props.yOffset, - props.targetWidth, - props.targetHeight, - props.maxWidth, - contentMeasuredWidth, - wrapperMeasuredHeight, - props.shiftHorizontal, - props.shiftVertical, - ], + [animation, windowWidth, xOffset, yOffset, targetWidth, targetHeight, maxWidth, contentMeasuredWidth, wrapperMeasuredHeight, shiftHorizontal, shiftVertical], ); let content; - if (props.renderTooltipContent) { - content = {props.renderTooltipContent()}; + if (renderTooltipContent) { + content = {renderTooltipContent()}; } else { content = ( - {props.text} + {text} ); } + const body = document.querySelector('body'); + + if (!body) { + return null; + } + return ReactDOM.createPortal( {content} @@ -143,12 +144,10 @@ function TooltipRenderedOnPageBody(props) { , - document.querySelector('body'), + body, ); } -TooltipRenderedOnPageBody.propTypes = propTypes; -TooltipRenderedOnPageBody.defaultProps = defaultProps; TooltipRenderedOnPageBody.displayName = 'TooltipRenderedOnPageBody'; export default React.memo(TooltipRenderedOnPageBody); diff --git a/src/components/Tooltip/TooltipSense.js b/src/components/Tooltip/TooltipSense.tsx similarity index 82% rename from src/components/Tooltip/TooltipSense.js rename to src/components/Tooltip/TooltipSense.tsx index 0e551e687cf3..bc16df3f8c7e 100644 --- a/src/components/Tooltip/TooltipSense.js +++ b/src/components/Tooltip/TooltipSense.tsx @@ -1,4 +1,4 @@ -import _ from 'underscore'; +import lodashDebounce from 'lodash/debounce'; import CONST from '@src/CONST'; let active = false; @@ -6,7 +6,7 @@ let active = false; /** * Debounced function to deactive the TooltipSense after a specific time */ -const debouncedDeactivate = _.debounce(() => { +const debouncedDeactivate = lodashDebounce(() => { active = false; }, CONST.TIMING.TOOLTIP_SENSE); diff --git a/src/components/Tooltip/index.js b/src/components/Tooltip/index.js deleted file mode 100644 index c7c4428e19a3..000000000000 --- a/src/components/Tooltip/index.js +++ /dev/null @@ -1,37 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import BaseTooltip from './BaseTooltip'; -import {defaultProps as tooltipDefaultProps, propTypes as tooltipPropTypes} from './tooltipPropTypes'; - -const propTypes = { - ...tooltipPropTypes, - - /** Whether the actual Tooltip should be rendered. If false, it's just going to return the children */ - shouldRender: PropTypes.bool, -}; - -const defaultProps = { - ...tooltipDefaultProps, - shouldRender: true, -}; - -function Tooltip({shouldRender = true, children, ...props}) { - if (!shouldRender) { - return children; - } - - return ( - - {children} - - ); -} - -Tooltip.displayName = 'Tooltip'; -Tooltip.propTypes = propTypes; -Tooltip.defaultProps = defaultProps; - -export default Tooltip; diff --git a/src/components/Tooltip/index.tsx b/src/components/Tooltip/index.tsx new file mode 100644 index 000000000000..87b8798ac77b --- /dev/null +++ b/src/components/Tooltip/index.tsx @@ -0,0 +1,26 @@ +import React from 'react'; +import BaseTooltip from './BaseTooltip'; +import {TooltipProps} from './types'; + +type Props = TooltipProps & { + shouldRender?: boolean; +}; + +function Tooltip({shouldRender = true, children, ...props}: Props) { + if (!shouldRender) { + return children; + } + + return ( + + {children} + + ); +} + +Tooltip.displayName = 'Tooltip'; + +export default Tooltip; diff --git a/src/components/Tooltip/tooltipPropTypes.js b/src/components/Tooltip/types.ts similarity index 50% rename from src/components/Tooltip/tooltipPropTypes.js rename to src/components/Tooltip/types.ts index 21df0df07f0d..e41366f95c21 100644 --- a/src/components/Tooltip/tooltipPropTypes.js +++ b/src/components/Tooltip/types.ts @@ -1,52 +1,37 @@ -import PropTypes from 'prop-types'; -import refPropTypes from '@components/refPropTypes'; -import variables from '@styles/variables'; -import CONST from '@src/CONST'; +import {ForwardedRef, ReactNode} from 'react'; -const propTypes = { +type TooltipProps = { /** The text to display in the tooltip. If text is ommitted, only children will be rendered. */ - text: PropTypes.string, + text: string; /** Maximum number of lines to show in tooltip */ - numberOfLines: PropTypes.number, + numberOfLines: number; /** Children to wrap with Tooltip. */ - children: PropTypes.node.isRequired, + children: ReactNode; /** Any additional amount to manually adjust the horizontal position of the tooltip. A positive value shifts the tooltip to the right, and a negative value shifts it to the left. */ - shiftHorizontal: PropTypes.oneOfType([PropTypes.number, PropTypes.func]), + shiftHorizontal: number | ((width: number) => number); /** Any additional amount to manually adjust the vertical position of the tooltip. A positive value shifts the tooltip down, and a negative value shifts it up. */ - shiftVertical: PropTypes.oneOfType([PropTypes.number, PropTypes.func]), + shiftVertical: number | ((height: number) => number); /** Number of pixels to set max-width on tooltip */ - maxWidth: PropTypes.number, + maxWidth: number; /** Render custom content inside the tooltip. Note: This cannot be used together with the text props. */ - renderTooltipContent: PropTypes.func, + renderTooltipContent: unknown; /** Unique key of renderTooltipContent to rerender the tooltip when one of the key changes */ - renderTooltipContentKey: PropTypes.arrayOf(PropTypes.string), + renderTooltipContentKey: string[]; /** passes this down to Hoverable component to decide whether to handle the scroll behaviour to show hover once the scroll ends */ - shouldHandleScroll: PropTypes.bool, + shouldHandleScroll: boolean; /** Reference to the tooltip container */ - tooltipRef: refPropTypes, + tooltipRef: ForwardedRef; }; -const defaultProps = { - shiftHorizontal: 0, - shiftVertical: 0, - text: '', - maxWidth: variables.sideBarWidth, - numberOfLines: CONST.TOOLTIP_MAX_LINES, - renderTooltipContent: undefined, - renderTooltipContentKey: [], - shouldHandleScroll: false, - tooltipRef: () => {}, -}; - -export {propTypes, defaultProps}; +export {TooltipProps}; From 6e995308411a0c1817070dfc5296b4ded65657f2 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Mon, 20 Nov 2023 11:41:20 +0100 Subject: [PATCH 02/26] fixes --- src/components/Tooltip/BaseTooltip.tsx | 26 +++++++++++++++---- .../Tooltip/TooltipRenderedOnPageBody.tsx | 4 --- src/components/Tooltip/types.ts | 6 ++--- 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/src/components/Tooltip/BaseTooltip.tsx b/src/components/Tooltip/BaseTooltip.tsx index bf99216bd827..6e37ae139573 100644 --- a/src/components/Tooltip/BaseTooltip.tsx +++ b/src/components/Tooltip/BaseTooltip.tsx @@ -53,7 +53,23 @@ function chooseBoundingBox(target: HTMLElement, clientX: number, clientY: number return target.getBoundingClientRect(); } -function Tooltip({children, numberOfLines, maxWidth = variables.sideBarWidth, text = '', renderTooltipContent, renderTooltipContentKey, shouldHandleScroll, shiftHorizontal = 0, shiftVertical = 0, tooltipRef}: TooltipProps) { +function callOrReturn(value: T | (() => T)): T { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return typeof value === 'function' ? value() : value; +} + +function Tooltip({ + numberOfLines, + maxWidth = variables.sideBarWidth, + text = '', + renderTooltipContent, + renderTooltipContentKey = [], + shouldHandleScroll, + shiftHorizontal = 0, + shiftVertical = 0, + tooltipRef, + children, +}: TooltipProps) { const {preferredLocale} = useLocalize(); const {windowWidth} = useWindowDimensions(); @@ -75,7 +91,7 @@ function Tooltip({children, numberOfLines, maxWidth = variables.sideBarWidth, te const isAnimationCanceled = useRef(false); const prevText = usePrevious(text); - const target = useRef(null); + const target = useRef(null); const initialMousePosition = useRef({x: 0, y: 0}); const updateTargetAndMousePosition = useCallback((e: MouseEvent) => { @@ -182,15 +198,15 @@ function Tooltip({children, numberOfLines, maxWidth = variables.sideBarWidth, te yOffset={yOffset} targetWidth={wrapperWidth} targetHeight={wrapperHeight} - shiftHorizontal={shiftHorizontal} - shiftVertical={shiftVertical} + shiftHorizontal={callOrReturn(shiftHorizontal)} + shiftVertical={callOrReturn(shiftVertical)} text={text} maxWidth={maxWidth} numberOfLines={numberOfLines} renderTooltipContent={renderTooltipContent} // We pass a key, so whenever the content changes this component will completely remount with a fresh state. // This prevents flickering/moving while remaining performant. - key={[text, ...renderTooltipContentKey, preferredLocale]} + key={[text, ...renderTooltipContentKey, preferredLocale].join('-')} /> )} number); + shiftHorizontal: number | (() => number); /** Any additional amount to manually adjust the vertical position of the tooltip. A positive value shifts the tooltip down, and a negative value shifts it up. */ - shiftVertical: number | ((height: number) => number); + shiftVertical: number | (() => number); /** Number of pixels to set max-width on tooltip */ maxWidth: number; /** Render custom content inside the tooltip. Note: This cannot be used together with the text props. */ - renderTooltipContent: unknown; + renderTooltipContent: () => ReactNode; /** Unique key of renderTooltipContent to rerender the tooltip when one of the key changes */ renderTooltipContentKey: string[]; From 929640021b96c733c5efde3a927d7e96a84439ac Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Mon, 20 Nov 2023 12:08:46 +0100 Subject: [PATCH 03/26] more fixes --- src/components/Hoverable/types.ts | 4 ++-- .../Pressable/PressableWithDelayToggle.tsx | 3 ++- src/components/Tooltip/BaseTooltip.tsx | 16 +++++++------ .../Tooltip/TooltipRenderedOnPageBody.tsx | 5 ++++ src/components/Tooltip/types.ts | 23 ++++++++++--------- 5 files changed, 30 insertions(+), 21 deletions(-) diff --git a/src/components/Hoverable/types.ts b/src/components/Hoverable/types.ts index aa38e0435b07..430b865f50c5 100644 --- a/src/components/Hoverable/types.ts +++ b/src/components/Hoverable/types.ts @@ -1,8 +1,8 @@ -import {ReactElement, ReactNode} from 'react'; +import {ReactElement} from 'react'; type HoverableProps = { /** Children to wrap with Hoverable. */ - children: ((isHovered: boolean) => ReactElement) | ReactNode; + children: ((isHovered: boolean) => ReactElement) | ReactElement; /** Whether to disable the hover action */ disabled?: boolean; diff --git a/src/components/Pressable/PressableWithDelayToggle.tsx b/src/components/Pressable/PressableWithDelayToggle.tsx index 7ded9320b802..5ee4215f24e3 100644 --- a/src/components/Pressable/PressableWithDelayToggle.tsx +++ b/src/components/Pressable/PressableWithDelayToggle.tsx @@ -103,7 +103,8 @@ function PressableWithDelayToggle( <> {inline && labelText} diff --git a/src/components/Tooltip/BaseTooltip.tsx b/src/components/Tooltip/BaseTooltip.tsx index 6e37ae139573..a204b08b9f8f 100644 --- a/src/components/Tooltip/BaseTooltip.tsx +++ b/src/components/Tooltip/BaseTooltip.tsx @@ -8,6 +8,7 @@ import usePrevious from '@hooks/usePrevious'; import useWindowDimensions from '@hooks/useWindowDimensions'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import variables from '@styles/variables'; +import CONST from '@src/CONST'; import TooltipRenderedOnPageBody from './TooltipRenderedOnPageBody'; import TooltipSense from './TooltipSense'; import {TooltipProps} from './types'; @@ -53,21 +54,22 @@ function chooseBoundingBox(target: HTMLElement, clientX: number, clientY: number return target.getBoundingClientRect(); } + +// TODO: Move to utils function callOrReturn(value: T | (() => T)): T { - // eslint-disable-next-line @typescript-eslint/no-unsafe-return - return typeof value === 'function' ? value() : value; + return typeof value === 'function' ? (value as () => T)() : value; } function Tooltip({ - numberOfLines, + numberOfLines = CONST.TOOLTIP_MAX_LINES, maxWidth = variables.sideBarWidth, text = '', - renderTooltipContent, + renderTooltipContent = undefined, renderTooltipContentKey = [], - shouldHandleScroll, + shouldHandleScroll = false, shiftHorizontal = 0, shiftVertical = 0, - tooltipRef, + tooltipRef = () => {}, children, }: TooltipProps) { const {preferredLocale} = useLocalize(); @@ -95,7 +97,7 @@ function Tooltip({ const initialMousePosition = useRef({x: 0, y: 0}); const updateTargetAndMousePosition = useCallback((e: MouseEvent) => { - target.current = e.currentTarget; + target.current = e.currentTarget as HTMLElement; initialMousePosition.current = {x: e.clientX, y: e.clientY}; }, []); diff --git a/src/components/Tooltip/TooltipRenderedOnPageBody.tsx b/src/components/Tooltip/TooltipRenderedOnPageBody.tsx index dfe03fdecdc7..72d8476c1842 100644 --- a/src/components/Tooltip/TooltipRenderedOnPageBody.tsx +++ b/src/components/Tooltip/TooltipRenderedOnPageBody.tsx @@ -46,6 +46,7 @@ type TooltipRenderedOnPageBodyProps = { renderTooltipContent?: () => React.ReactNode; }; +// TODO: Move to utils const viewRef = (ref: React.RefObject) => ref as React.RefObject; // Props will change frequently. @@ -130,6 +131,10 @@ function TooltipRenderedOnPageBody({ const body = document.querySelector('body'); + if (!body) { + return null; + } + return ReactDOM.createPortal( number); + shiftHorizontal?: number | (() => number); /** Any additional amount to manually adjust the vertical position of the tooltip. A positive value shifts the tooltip down, and a negative value shifts it up. */ - shiftVertical: number | (() => number); + shiftVertical?: number | (() => number); /** Number of pixels to set max-width on tooltip */ - maxWidth: number; + maxWidth?: number; /** Render custom content inside the tooltip. Note: This cannot be used together with the text props. */ - renderTooltipContent: () => ReactNode; + renderTooltipContent?: () => ReactNode; /** Unique key of renderTooltipContent to rerender the tooltip when one of the key changes */ - renderTooltipContentKey: string[]; + renderTooltipContentKey?: string[]; /** passes this down to Hoverable component to decide whether to handle the scroll behaviour to show hover once the scroll ends */ - shouldHandleScroll: boolean; + shouldHandleScroll?: boolean; /** Reference to the tooltip container */ - tooltipRef: ForwardedRef; + tooltipRef?: LegacyRef; }; export {TooltipProps}; From 8a97ea0af0f69c155d675979f220748ccf62568e Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Mon, 20 Nov 2023 12:34:42 +0100 Subject: [PATCH 04/26] fix refs --- .../DisplayNames/DisplayNamesTooltipItem.tsx | 5 +++-- src/components/Tooltip/BaseTooltip.tsx | 3 +-- src/components/Tooltip/PopoverAnchorTooltip.tsx | 16 ++++++---------- src/components/Tooltip/index.tsx | 8 ++------ src/components/Tooltip/types.ts | 13 +++++++++---- 5 files changed, 21 insertions(+), 24 deletions(-) diff --git a/src/components/DisplayNames/DisplayNamesTooltipItem.tsx b/src/components/DisplayNames/DisplayNamesTooltipItem.tsx index 82f9c5799b78..dec31181b7c9 100644 --- a/src/components/DisplayNames/DisplayNamesTooltipItem.tsx +++ b/src/components/DisplayNames/DisplayNamesTooltipItem.tsx @@ -1,4 +1,4 @@ -import React, {RefObject, useCallback} from 'react'; +import React, {RefObject, useMemo} from 'react'; import {Text as RNText, StyleProp, TextStyle} from 'react-native'; import Text from '@components/Text'; import UserDetailsTooltip from '@components/UserDetailsTooltip'; @@ -41,7 +41,8 @@ function DisplayNamesTooltipItem({ childRefs = {current: []}, }: DisplayNamesTooltipItemProps) { const styles = useThemeStyles(); - const tooltipIndexBridge = useCallback(() => getTooltipShiftX(index), [getTooltipShiftX, index]); + // TODO: Check here if we need to memoize the tooltipIndexBridge + const tooltipIndexBridge = useMemo(() => getTooltipShiftX(index), [getTooltipShiftX, index]); return ( (value: T | (() => T)): T { return typeof value === 'function' ? (value as () => T)() : value; @@ -69,7 +68,7 @@ function Tooltip({ shouldHandleScroll = false, shiftHorizontal = 0, shiftVertical = 0, - tooltipRef = () => {}, + tooltipRef, children, }: TooltipProps) { const {preferredLocale} = useLocalize(); diff --git a/src/components/Tooltip/PopoverAnchorTooltip.tsx b/src/components/Tooltip/PopoverAnchorTooltip.tsx index 748cf5d9e229..5756063216e8 100644 --- a/src/components/Tooltip/PopoverAnchorTooltip.tsx +++ b/src/components/Tooltip/PopoverAnchorTooltip.tsx @@ -1,20 +1,16 @@ +import {BoundsObserver} from '@react-ng/bounds-observer'; import React, {useContext, useMemo, useRef} from 'react'; -import {View} from 'react-native'; import {PopoverContext} from '@components/PopoverProvider'; import BaseTooltip from './BaseTooltip'; -import {TooltipProps} from './types'; +import {TooltipExtendedProps} from './types'; -type Props = TooltipProps & { - /** Whether the actual Tooltip should be rendered. If false, it's just going to return the children */ - shouldRender: boolean; -}; -function PopoverAnchorTooltip({shouldRender = true, children, ...props}: Props) { +function PopoverAnchorTooltip({shouldRender = true, children, ...props}: TooltipExtendedProps) { const {isOpen, popover} = useContext(PopoverContext); - const tooltipRef = useRef(null); + const tooltipRef = useRef(null); const isPopoverRelatedToTooltipOpen = useMemo(() => { - // eslint-disable-next-line - const tooltipNode = tooltipRef.current ? tooltipRef.current._childNode : null; + // eslint-disable-next-line @typescript-eslint/dot-notation + const tooltipNode = tooltipRef.current ? tooltipRef.current['_childNode'] : null; if (isOpen && popover?.anchorRef?.current && tooltipNode && (tooltipNode.contains(popover.anchorRef.current) || tooltipNode === popover.anchorRef.current)) { return true; } diff --git a/src/components/Tooltip/index.tsx b/src/components/Tooltip/index.tsx index 87b8798ac77b..6d274b87badb 100644 --- a/src/components/Tooltip/index.tsx +++ b/src/components/Tooltip/index.tsx @@ -1,12 +1,8 @@ import React from 'react'; import BaseTooltip from './BaseTooltip'; -import {TooltipProps} from './types'; +import {TooltipExtendedProps} from './types'; -type Props = TooltipProps & { - shouldRender?: boolean; -}; - -function Tooltip({shouldRender = true, children, ...props}: Props) { +function Tooltip({shouldRender = true, children, ...props}: TooltipExtendedProps) { if (!shouldRender) { return children; } diff --git a/src/components/Tooltip/types.ts b/src/components/Tooltip/types.ts index 75695d4b43bb..633a5882fb34 100644 --- a/src/components/Tooltip/types.ts +++ b/src/components/Tooltip/types.ts @@ -1,5 +1,5 @@ -import { BoundsObserver } from '@react-ng/bounds-observer'; -import {LegacyRef, ReactElement, ReactNode} from 'react'; +import {BoundsObserver} from '@react-ng/bounds-observer'; +import {ReactElement, ReactNode, RefObject} from 'react'; type TooltipProps = { /** The text to display in the tooltip. If text is ommitted, only children will be rendered. */ @@ -32,7 +32,12 @@ type TooltipProps = { shouldHandleScroll?: boolean; /** Reference to the tooltip container */ - tooltipRef?: LegacyRef; + tooltipRef?: RefObject | null; }; -export {TooltipProps}; +type TooltipExtendedProps = TooltipProps & { + /** Whether the actual Tooltip should be rendered. If false, it's just going to return the children */ + shouldRender?: boolean; +}; + +export type {TooltipProps, TooltipExtendedProps}; From 15d3837fc37a9629211791d48543384ab5921dca Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Mon, 20 Nov 2023 13:45:56 +0100 Subject: [PATCH 05/26] remove unknown type --- src/components/Tooltip/BaseTooltip.tsx | 6 +----- .../Tooltip/TooltipRenderedOnPageBody.tsx | 3 +-- src/styles/getTooltipStyles.ts | 14 +++++++------- src/types/utils/callOrReturn.ts | 5 +++++ 4 files changed, 14 insertions(+), 14 deletions(-) create mode 100644 src/types/utils/callOrReturn.ts diff --git a/src/components/Tooltip/BaseTooltip.tsx b/src/components/Tooltip/BaseTooltip.tsx index 55f4d7a49851..72c8ae4e3f74 100644 --- a/src/components/Tooltip/BaseTooltip.tsx +++ b/src/components/Tooltip/BaseTooltip.tsx @@ -9,6 +9,7 @@ import useWindowDimensions from '@hooks/useWindowDimensions'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import variables from '@styles/variables'; import CONST from '@src/CONST'; +import callOrReturn from '@src/types/utils/callOrReturn'; import TooltipRenderedOnPageBody from './TooltipRenderedOnPageBody'; import TooltipSense from './TooltipSense'; import {TooltipProps} from './types'; @@ -54,11 +55,6 @@ function chooseBoundingBox(target: HTMLElement, clientX: number, clientY: number return target.getBoundingClientRect(); } -// TODO: Move to utils -function callOrReturn(value: T | (() => T)): T { - return typeof value === 'function' ? (value as () => T)() : value; -} - function Tooltip({ numberOfLines = CONST.TOOLTIP_MAX_LINES, maxWidth = variables.sideBarWidth, diff --git a/src/components/Tooltip/TooltipRenderedOnPageBody.tsx b/src/components/Tooltip/TooltipRenderedOnPageBody.tsx index 72d8476c1842..c24987f772a4 100644 --- a/src/components/Tooltip/TooltipRenderedOnPageBody.tsx +++ b/src/components/Tooltip/TooltipRenderedOnPageBody.tsx @@ -10,8 +10,7 @@ type TooltipRenderedOnPageBodyProps = { windowWidth: number; /** Tooltip Animation value */ - // eslint-disable-next-line react/forbid-prop-types - animation: unknown; + animation: Animated.Value; /** The distance between the left side of the wrapper view and the left side of the window */ xOffset: number; diff --git a/src/styles/getTooltipStyles.ts b/src/styles/getTooltipStyles.ts index 5c90027e10fd..707e77310acd 100644 --- a/src/styles/getTooltipStyles.ts +++ b/src/styles/getTooltipStyles.ts @@ -1,4 +1,4 @@ -import {TextStyle, View, ViewStyle} from 'react-native'; +import {Animated, TextStyle, View, ViewStyle} from 'react-native'; import fontFamily from './fontFamily'; import roundToNearestMultipleOfFour from './roundToNearestMultipleOfFour'; import styles from './styles'; @@ -123,16 +123,16 @@ type TooltipStyles = { * A positive value shifts it down, and a negative value shifts it up. */ export default function getTooltipStyles( - tooltip: View | HTMLDivElement, - currentSize: number, + tooltip: View | HTMLDivElement | null, + currentSize: Animated.Value, windowWidth: number, xOffset: number, yOffset: number, tooltipTargetWidth: number, tooltipTargetHeight: number, maxWidth: number, - tooltipContentWidth: number, - tooltipWrapperHeight: number, + tooltipContentWidth = 0, + tooltipWrapperHeight = 0, manualShiftHorizontal = 0, manualShiftVertical = 0, ): TooltipStyles { @@ -147,7 +147,7 @@ export default function getTooltipStyles( const isTooltipSizeReady = tooltipWidth !== undefined && tooltipHeight !== undefined; // Set the scale to 1 to be able to measure the tooltip size correctly when it's not ready yet. - let scale = 1; + let scale = new Animated.Value(1); let shouldShowBelow = false; let horizontalShift = 0; let horizontalShiftPointer = 0; @@ -162,7 +162,7 @@ export default function getTooltipStyles( // If either a tooltip will try to render within GUTTER_WIDTH logical pixels of the top of the screen, // Or the wrapped component is overlapping at top-center with another element // we'll display it beneath its wrapped component rather than above it as usual. - shouldShowBelow = yOffset - tooltipHeight < GUTTER_WIDTH || isOverlappingAtTop(tooltip, xOffset, yOffset, tooltipTargetWidth, tooltipTargetHeight); + shouldShowBelow = yOffset - tooltipHeight < GUTTER_WIDTH || !!(tooltip && isOverlappingAtTop(tooltip, xOffset, yOffset, tooltipTargetWidth, tooltipTargetHeight)); // When the tooltip size is ready, we can start animating the scale. scale = currentSize; diff --git a/src/types/utils/callOrReturn.ts b/src/types/utils/callOrReturn.ts new file mode 100644 index 000000000000..e0b2b9082a06 --- /dev/null +++ b/src/types/utils/callOrReturn.ts @@ -0,0 +1,5 @@ +function callOrReturn(value: T | (() => T)): T { + return typeof value === 'function' ? (value as () => T)() : value; +} + +export default callOrReturn; From 0f81bb02deff56c2b003dc5d117e4d1f08f46e2d Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Mon, 20 Nov 2023 13:19:52 +0100 Subject: [PATCH 06/26] refactor --- src/components/Tooltip/TooltipRenderedOnPageBody.tsx | 4 +--- src/types/utils/viewRef.ts | 5 +++++ 2 files changed, 6 insertions(+), 3 deletions(-) create mode 100644 src/types/utils/viewRef.ts diff --git a/src/components/Tooltip/TooltipRenderedOnPageBody.tsx b/src/components/Tooltip/TooltipRenderedOnPageBody.tsx index c24987f772a4..4e21ca92e373 100644 --- a/src/components/Tooltip/TooltipRenderedOnPageBody.tsx +++ b/src/components/Tooltip/TooltipRenderedOnPageBody.tsx @@ -4,6 +4,7 @@ import {Animated, View} from 'react-native'; import Text from '@components/Text'; import Log from '@libs/Log'; import getTooltipStyles from '@styles/getTooltipStyles'; +import viewRef from '@src/types/utils/viewRef'; type TooltipRenderedOnPageBodyProps = { /** Window width */ @@ -45,9 +46,6 @@ type TooltipRenderedOnPageBodyProps = { renderTooltipContent?: () => React.ReactNode; }; -// TODO: Move to utils -const viewRef = (ref: React.RefObject) => ref as React.RefObject; - // Props will change frequently. // On every tooltip hover, we update the position in state which will result in re-rendering. // We also update the state on layout changes which will be triggered often. diff --git a/src/types/utils/viewRef.ts b/src/types/utils/viewRef.ts new file mode 100644 index 000000000000..015d88cc5a8b --- /dev/null +++ b/src/types/utils/viewRef.ts @@ -0,0 +1,5 @@ +import {View} from 'react-native'; + +const viewRef = (ref: React.RefObject) => ref as React.RefObject; + +export default viewRef; From 92480256b6e93b079cb7a7ee10398e1f22bce40a Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Mon, 20 Nov 2023 14:10:03 +0100 Subject: [PATCH 07/26] Change order of props in Tooltip --- src/components/Tooltip/BaseTooltip.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Tooltip/BaseTooltip.tsx b/src/components/Tooltip/BaseTooltip.tsx index 72c8ae4e3f74..129b4fe4be23 100644 --- a/src/components/Tooltip/BaseTooltip.tsx +++ b/src/components/Tooltip/BaseTooltip.tsx @@ -56,6 +56,7 @@ function chooseBoundingBox(target: HTMLElement, clientX: number, clientY: number } function Tooltip({ + children, numberOfLines = CONST.TOOLTIP_MAX_LINES, maxWidth = variables.sideBarWidth, text = '', @@ -65,7 +66,6 @@ function Tooltip({ shiftHorizontal = 0, shiftVertical = 0, tooltipRef, - children, }: TooltipProps) { const {preferredLocale} = useLocalize(); const {windowWidth} = useWindowDimensions(); From 3f1ba1f21936141ddfae582712616861b4efc673 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Mon, 20 Nov 2023 14:12:29 +0100 Subject: [PATCH 08/26] fix --- src/components/Tooltip/BaseTooltip.tsx | 4 ++-- src/components/Tooltip/TooltipRenderedOnPageBody.tsx | 9 +++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/components/Tooltip/BaseTooltip.tsx b/src/components/Tooltip/BaseTooltip.tsx index 129b4fe4be23..e1536ed4481c 100644 --- a/src/components/Tooltip/BaseTooltip.tsx +++ b/src/components/Tooltip/BaseTooltip.tsx @@ -195,8 +195,8 @@ function Tooltip({ yOffset={yOffset} targetWidth={wrapperWidth} targetHeight={wrapperHeight} - shiftHorizontal={callOrReturn(shiftHorizontal)} - shiftVertical={callOrReturn(shiftVertical)} + shiftHorizontal={shiftHorizontal} + shiftVertical={shiftVertical} text={text} maxWidth={maxWidth} numberOfLines={numberOfLines} diff --git a/src/components/Tooltip/TooltipRenderedOnPageBody.tsx b/src/components/Tooltip/TooltipRenderedOnPageBody.tsx index 4e21ca92e373..28a9847cd05b 100644 --- a/src/components/Tooltip/TooltipRenderedOnPageBody.tsx +++ b/src/components/Tooltip/TooltipRenderedOnPageBody.tsx @@ -4,6 +4,7 @@ import {Animated, View} from 'react-native'; import Text from '@components/Text'; import Log from '@libs/Log'; import getTooltipStyles from '@styles/getTooltipStyles'; +import callOrReturn from '@src/types/utils/callOrReturn'; import viewRef from '@src/types/utils/viewRef'; type TooltipRenderedOnPageBodyProps = { @@ -27,11 +28,11 @@ type TooltipRenderedOnPageBodyProps = { /** Any additional amount to manually adjust the horizontal position of the tooltip. A positive value shifts the tooltip to the right, and a negative value shifts it to the left. */ - shiftHorizontal?: number; + shiftHorizontal?: (() => number) | number; /** Any additional amount to manually adjust the vertical position of the tooltip. A positive value shifts the tooltip down, and a negative value shifts it up. */ - shiftVertical?: number; + shiftVertical?: (() => number) | number; /** Text to be shown in the tooltip */ text: string; @@ -101,8 +102,8 @@ function TooltipRenderedOnPageBody({ maxWidth, contentMeasuredWidth, wrapperMeasuredHeight, - shiftHorizontal, - shiftVertical, + callOrReturn(shiftHorizontal), + callOrReturn(shiftVertical), ), [animation, windowWidth, xOffset, yOffset, targetWidth, targetHeight, maxWidth, contentMeasuredWidth, wrapperMeasuredHeight, shiftHorizontal, shiftVertical], ); From 88f20f320fceb8887df44815da72fb6003cadea5 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Mon, 20 Nov 2023 14:14:07 +0100 Subject: [PATCH 09/26] minor changes --- src/components/Tooltip/BaseTooltip.tsx | 1 - src/components/Tooltip/TooltipRenderedOnPageBody.tsx | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/Tooltip/BaseTooltip.tsx b/src/components/Tooltip/BaseTooltip.tsx index e1536ed4481c..e13f5e0314ef 100644 --- a/src/components/Tooltip/BaseTooltip.tsx +++ b/src/components/Tooltip/BaseTooltip.tsx @@ -9,7 +9,6 @@ import useWindowDimensions from '@hooks/useWindowDimensions'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import variables from '@styles/variables'; import CONST from '@src/CONST'; -import callOrReturn from '@src/types/utils/callOrReturn'; import TooltipRenderedOnPageBody from './TooltipRenderedOnPageBody'; import TooltipSense from './TooltipSense'; import {TooltipProps} from './types'; diff --git a/src/components/Tooltip/TooltipRenderedOnPageBody.tsx b/src/components/Tooltip/TooltipRenderedOnPageBody.tsx index 28a9847cd05b..fab14c311282 100644 --- a/src/components/Tooltip/TooltipRenderedOnPageBody.tsx +++ b/src/components/Tooltip/TooltipRenderedOnPageBody.tsx @@ -64,7 +64,7 @@ function TooltipRenderedOnPageBody({ text, numberOfLines, maxWidth = 0, - renderTooltipContent, + renderTooltipContent = undefined, }: TooltipRenderedOnPageBodyProps) { // The width of tooltip's inner content. Has to be undefined in the beginning // as a width of 0 will cause the content to be rendered of a width of 0, From 9c65d98fa32b9a9eb2217711e3523c06ccec621c Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Mon, 20 Nov 2023 14:20:44 +0100 Subject: [PATCH 10/26] reuse some types --- .../Tooltip/TooltipRenderedOnPageBody.tsx | 23 ++----------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/src/components/Tooltip/TooltipRenderedOnPageBody.tsx b/src/components/Tooltip/TooltipRenderedOnPageBody.tsx index fab14c311282..bbf2406b8e2d 100644 --- a/src/components/Tooltip/TooltipRenderedOnPageBody.tsx +++ b/src/components/Tooltip/TooltipRenderedOnPageBody.tsx @@ -6,6 +6,7 @@ import Log from '@libs/Log'; import getTooltipStyles from '@styles/getTooltipStyles'; import callOrReturn from '@src/types/utils/callOrReturn'; import viewRef from '@src/types/utils/viewRef'; +import {TooltipProps} from './types'; type TooltipRenderedOnPageBodyProps = { /** Window width */ @@ -25,27 +26,7 @@ type TooltipRenderedOnPageBodyProps = { /** The height of the tooltip's target */ targetHeight: number; - - /** Any additional amount to manually adjust the horizontal position of the tooltip. - A positive value shifts the tooltip to the right, and a negative value shifts it to the left. */ - shiftHorizontal?: (() => number) | number; - - /** Any additional amount to manually adjust the vertical position of the tooltip. - A positive value shifts the tooltip down, and a negative value shifts it up. */ - shiftVertical?: (() => number) | number; - - /** Text to be shown in the tooltip */ - text: string; - - /** Maximum number of lines to show in tooltip */ - numberOfLines: number; - - /** Number of pixels to set max-width on tooltip */ - maxWidth?: number; - - /** Render custom content inside the tooltip. Note: This cannot be used together with the text props. */ - renderTooltipContent?: () => React.ReactNode; -}; +} & Pick; // Props will change frequently. // On every tooltip hover, we update the position in state which will result in re-rendering. From 542a6d3c4255b6f67e98ff8ad6eb51c5df812998 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Mon, 20 Nov 2023 15:10:09 +0100 Subject: [PATCH 11/26] fix --- src/styles/getTooltipStyles.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/styles/getTooltipStyles.ts b/src/styles/getTooltipStyles.ts index 707e77310acd..6cff3170be40 100644 --- a/src/styles/getTooltipStyles.ts +++ b/src/styles/getTooltipStyles.ts @@ -131,8 +131,8 @@ export default function getTooltipStyles( tooltipTargetWidth: number, tooltipTargetHeight: number, maxWidth: number, - tooltipContentWidth = 0, - tooltipWrapperHeight = 0, + tooltipContentWidth?: number, + tooltipWrapperHeight?: number, manualShiftHorizontal = 0, manualShiftVertical = 0, ): TooltipStyles { From 316fc40852a68c2e2e410d8b73ee9b85db2af38e Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Thu, 23 Nov 2023 16:07:48 +0100 Subject: [PATCH 12/26] Add textRef util --- src/components/Tooltip/TooltipRenderedOnPageBody.tsx | 3 ++- src/types/utils/textRef.ts | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 src/types/utils/textRef.ts diff --git a/src/components/Tooltip/TooltipRenderedOnPageBody.tsx b/src/components/Tooltip/TooltipRenderedOnPageBody.tsx index bbf2406b8e2d..14110e1b04c1 100644 --- a/src/components/Tooltip/TooltipRenderedOnPageBody.tsx +++ b/src/components/Tooltip/TooltipRenderedOnPageBody.tsx @@ -5,6 +5,7 @@ import Text from '@components/Text'; import Log from '@libs/Log'; import getTooltipStyles from '@styles/getTooltipStyles'; import callOrReturn from '@src/types/utils/callOrReturn'; +import textRef from '@src/types/utils/textRef'; import viewRef from '@src/types/utils/viewRef'; import {TooltipProps} from './types'; @@ -100,7 +101,7 @@ function TooltipRenderedOnPageBody({ > {text} diff --git a/src/types/utils/textRef.ts b/src/types/utils/textRef.ts new file mode 100644 index 000000000000..df0235f4ac0b --- /dev/null +++ b/src/types/utils/textRef.ts @@ -0,0 +1,5 @@ +import {Text} from 'react-native'; + +const textRef = (ref: React.RefObject) => ref as React.RefObject; + +export default textRef; From b401d933c4f7da9c78d86d4b01217c0eac5d56e2 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Mon, 27 Nov 2023 09:26:51 +0100 Subject: [PATCH 13/26] use children props --- src/components/Tooltip/BaseTooltip.native.tsx | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/components/Tooltip/BaseTooltip.native.tsx b/src/components/Tooltip/BaseTooltip.native.tsx index 7f54f5cb9668..6d3938940fd8 100644 --- a/src/components/Tooltip/BaseTooltip.native.tsx +++ b/src/components/Tooltip/BaseTooltip.native.tsx @@ -1,11 +1,7 @@ // We can't use the common component for the Tooltip as Web implementation uses DOM specific method to -import {ReactNode} from 'react'; +import ChildrenProps from '@src/types/utils/ChildrenProps'; -// render the View which is not present on the Mobile. -type TooltipProps = { - children: ReactNode; -}; -function Tooltip({children}: TooltipProps) { +function Tooltip({children}: ChildrenProps) { return children; } From ac0d976428b79a2c15b094df1ea1e52329a28159 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Mon, 27 Nov 2023 09:29:34 +0100 Subject: [PATCH 14/26] address review --- src/components/Tooltip/BaseTooltip.tsx | 13 +++++++------ .../Tooltip/TooltipRenderedOnPageBody.tsx | 19 +++++++++++++------ src/components/Tooltip/types.ts | 3 ++- src/types/utils/callOrReturn.ts | 4 ++-- 4 files changed, 24 insertions(+), 15 deletions(-) diff --git a/src/components/Tooltip/BaseTooltip.tsx b/src/components/Tooltip/BaseTooltip.tsx index e13f5e0314ef..6bb9977cb6b2 100644 --- a/src/components/Tooltip/BaseTooltip.tsx +++ b/src/components/Tooltip/BaseTooltip.tsx @@ -1,5 +1,4 @@ import {BoundsObserver} from '@react-ng/bounds-observer'; -import _ from 'lodash'; import React, {memo, useCallback, useEffect, useRef, useState} from 'react'; import {Animated} from 'react-native'; import Hoverable from '@components/Hoverable'; @@ -9,9 +8,11 @@ import useWindowDimensions from '@hooks/useWindowDimensions'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import variables from '@styles/variables'; import CONST from '@src/CONST'; +import callOrReturn from '@src/types/utils/callOrReturn'; +import StringUtils from '@src/libs/StringUtils'; import TooltipRenderedOnPageBody from './TooltipRenderedOnPageBody'; import TooltipSense from './TooltipSense'; -import {TooltipProps} from './types'; +import TooltipProps from './types'; const hasHoverSupport = DeviceCapabilities.hasHoverSupport(); @@ -59,7 +60,7 @@ function Tooltip({ numberOfLines = CONST.TOOLTIP_MAX_LINES, maxWidth = variables.sideBarWidth, text = '', - renderTooltipContent = undefined, + renderTooltipContent, renderTooltipContentKey = [], shouldHandleScroll = false, shiftHorizontal = 0, @@ -180,7 +181,7 @@ function Tooltip({ // Skip the tooltip and return the children if the text is empty, // we don't have a render function or the device does not support hovering - if ((_.isEmpty(text) && renderTooltipContent == null) || !hasHoverSupport) { + if ((StringUtils.isEmptyString(text) && renderTooltipContent == null) || !hasHoverSupport) { return children; } @@ -194,8 +195,8 @@ function Tooltip({ yOffset={yOffset} targetWidth={wrapperWidth} targetHeight={wrapperHeight} - shiftHorizontal={shiftHorizontal} - shiftVertical={shiftVertical} + shiftHorizontal={callOrReturn(shiftHorizontal)} + shiftVertical={callOrReturn(shiftVertical)} text={text} maxWidth={maxWidth} numberOfLines={numberOfLines} diff --git a/src/components/Tooltip/TooltipRenderedOnPageBody.tsx b/src/components/Tooltip/TooltipRenderedOnPageBody.tsx index 14110e1b04c1..816df53f2848 100644 --- a/src/components/Tooltip/TooltipRenderedOnPageBody.tsx +++ b/src/components/Tooltip/TooltipRenderedOnPageBody.tsx @@ -4,10 +4,9 @@ import {Animated, View} from 'react-native'; import Text from '@components/Text'; import Log from '@libs/Log'; import getTooltipStyles from '@styles/getTooltipStyles'; -import callOrReturn from '@src/types/utils/callOrReturn'; import textRef from '@src/types/utils/textRef'; import viewRef from '@src/types/utils/viewRef'; -import {TooltipProps} from './types'; +import TooltipProps from './types'; type TooltipRenderedOnPageBodyProps = { /** Window width */ @@ -27,7 +26,15 @@ type TooltipRenderedOnPageBodyProps = { /** The height of the tooltip's target */ targetHeight: number; -} & Pick; + + /** Any additional amount to manually adjust the horizontal position of the tooltip. + A positive value shifts the tooltip to the right, and a negative value shifts it to the left. */ + shiftHorizontal: number; + + /** Any additional amount to manually adjust the vertical position of the tooltip. + A positive value shifts the tooltip down, and a negative value shifts it up. */ + shiftVertical: number; +} & Pick; // Props will change frequently. // On every tooltip hover, we update the position in state which will result in re-rendering. @@ -46,7 +53,7 @@ function TooltipRenderedOnPageBody({ text, numberOfLines, maxWidth = 0, - renderTooltipContent = undefined, + renderTooltipContent, }: TooltipRenderedOnPageBodyProps) { // The width of tooltip's inner content. Has to be undefined in the beginning // as a width of 0 will cause the content to be rendered of a width of 0, @@ -84,8 +91,8 @@ function TooltipRenderedOnPageBody({ maxWidth, contentMeasuredWidth, wrapperMeasuredHeight, - callOrReturn(shiftHorizontal), - callOrReturn(shiftVertical), + shiftHorizontal, + shiftVertical, ), [animation, windowWidth, xOffset, yOffset, targetWidth, targetHeight, maxWidth, contentMeasuredWidth, wrapperMeasuredHeight, shiftHorizontal, shiftVertical], ); diff --git a/src/components/Tooltip/types.ts b/src/components/Tooltip/types.ts index 633a5882fb34..6388c14741ab 100644 --- a/src/components/Tooltip/types.ts +++ b/src/components/Tooltip/types.ts @@ -40,4 +40,5 @@ type TooltipExtendedProps = TooltipProps & { shouldRender?: boolean; }; -export type {TooltipProps, TooltipExtendedProps}; +export default TooltipProps; +export type {TooltipExtendedProps}; diff --git a/src/types/utils/callOrReturn.ts b/src/types/utils/callOrReturn.ts index e0b2b9082a06..985a752ad68c 100644 --- a/src/types/utils/callOrReturn.ts +++ b/src/types/utils/callOrReturn.ts @@ -1,5 +1,5 @@ -function callOrReturn(value: T | (() => T)): T { - return typeof value === 'function' ? (value as () => T)() : value; +function callOrReturn(value: TValue | (() => TValue)): TValue { + return typeof value === 'function' ? (value as () => TValue)() : value; } export default callOrReturn; From cc802694f39bb345f2722b377c1d6c657e7d7f61 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Mon, 27 Nov 2023 09:50:10 +0100 Subject: [PATCH 15/26] use forwardRef --- src/components/Tooltip/BaseTooltip.tsx | 34 ++++++++++--------- .../Tooltip/PopoverAnchorTooltip.tsx | 2 +- src/components/Tooltip/types.ts | 6 +--- 3 files changed, 20 insertions(+), 22 deletions(-) diff --git a/src/components/Tooltip/BaseTooltip.tsx b/src/components/Tooltip/BaseTooltip.tsx index 6bb9977cb6b2..27513984cd29 100644 --- a/src/components/Tooltip/BaseTooltip.tsx +++ b/src/components/Tooltip/BaseTooltip.tsx @@ -1,5 +1,5 @@ import {BoundsObserver} from '@react-ng/bounds-observer'; -import React, {memo, useCallback, useEffect, useRef, useState} from 'react'; +import React, {ForwardedRef, forwardRef, memo, useCallback, useEffect, useRef, useState} from 'react'; import {Animated} from 'react-native'; import Hoverable from '@components/Hoverable'; import useLocalize from '@hooks/useLocalize'; @@ -8,8 +8,8 @@ import useWindowDimensions from '@hooks/useWindowDimensions'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import variables from '@styles/variables'; import CONST from '@src/CONST'; -import callOrReturn from '@src/types/utils/callOrReturn'; import StringUtils from '@src/libs/StringUtils'; +import callOrReturn from '@src/types/utils/callOrReturn'; import TooltipRenderedOnPageBody from './TooltipRenderedOnPageBody'; import TooltipSense from './TooltipSense'; import TooltipProps from './types'; @@ -55,18 +55,20 @@ function chooseBoundingBox(target: HTMLElement, clientX: number, clientY: number return target.getBoundingClientRect(); } -function Tooltip({ - children, - numberOfLines = CONST.TOOLTIP_MAX_LINES, - maxWidth = variables.sideBarWidth, - text = '', - renderTooltipContent, - renderTooltipContentKey = [], - shouldHandleScroll = false, - shiftHorizontal = 0, - shiftVertical = 0, - tooltipRef, -}: TooltipProps) { +function Tooltip( + { + children, + numberOfLines = CONST.TOOLTIP_MAX_LINES, + maxWidth = variables.sideBarWidth, + text = '', + renderTooltipContent, + renderTooltipContentKey = [], + shouldHandleScroll = false, + shiftHorizontal = 0, + shiftVertical = 0, + }: TooltipProps, + ref: ForwardedRef, +) { const {preferredLocale} = useLocalize(); const {windowWidth} = useWindowDimensions(); @@ -209,7 +211,7 @@ function Tooltip({ {children} diff --git a/src/components/Tooltip/types.ts b/src/components/Tooltip/types.ts index 6388c14741ab..df95c3ea5979 100644 --- a/src/components/Tooltip/types.ts +++ b/src/components/Tooltip/types.ts @@ -1,5 +1,4 @@ -import {BoundsObserver} from '@react-ng/bounds-observer'; -import {ReactElement, ReactNode, RefObject} from 'react'; +import {ReactElement, ReactNode} from 'react'; type TooltipProps = { /** The text to display in the tooltip. If text is ommitted, only children will be rendered. */ @@ -30,9 +29,6 @@ type TooltipProps = { /** passes this down to Hoverable component to decide whether to handle the scroll behaviour to show hover once the scroll ends */ shouldHandleScroll?: boolean; - - /** Reference to the tooltip container */ - tooltipRef?: RefObject | null; }; type TooltipExtendedProps = TooltipProps & { From bc65c469f9d6be6eded46a0dd015d44bbde8a26c Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Mon, 27 Nov 2023 09:51:28 +0100 Subject: [PATCH 16/26] address review --- src/components/DisplayNames/DisplayNamesTooltipItem.tsx | 5 ++--- src/components/Pressable/PressableWithDelayToggle.tsx | 2 -- src/components/Tooltip/BaseTooltip.tsx | 5 ++++- src/components/Tooltip/PopoverAnchorTooltip.tsx | 2 +- src/types/utils/callOrReturn.ts | 6 +++++- 5 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/components/DisplayNames/DisplayNamesTooltipItem.tsx b/src/components/DisplayNames/DisplayNamesTooltipItem.tsx index dec31181b7c9..82f9c5799b78 100644 --- a/src/components/DisplayNames/DisplayNamesTooltipItem.tsx +++ b/src/components/DisplayNames/DisplayNamesTooltipItem.tsx @@ -1,4 +1,4 @@ -import React, {RefObject, useMemo} from 'react'; +import React, {RefObject, useCallback} from 'react'; import {Text as RNText, StyleProp, TextStyle} from 'react-native'; import Text from '@components/Text'; import UserDetailsTooltip from '@components/UserDetailsTooltip'; @@ -41,8 +41,7 @@ function DisplayNamesTooltipItem({ childRefs = {current: []}, }: DisplayNamesTooltipItemProps) { const styles = useThemeStyles(); - // TODO: Check here if we need to memoize the tooltipIndexBridge - const tooltipIndexBridge = useMemo(() => getTooltipShiftX(index), [getTooltipShiftX, index]); + const tooltipIndexBridge = useCallback(() => getTooltipShiftX(index), [getTooltipShiftX, index]); return ( {inline && labelText} diff --git a/src/components/Tooltip/BaseTooltip.tsx b/src/components/Tooltip/BaseTooltip.tsx index 27513984cd29..896f250c8b8b 100644 --- a/src/components/Tooltip/BaseTooltip.tsx +++ b/src/components/Tooltip/BaseTooltip.tsx @@ -94,7 +94,10 @@ function Tooltip( const initialMousePosition = useRef({x: 0, y: 0}); const updateTargetAndMousePosition = useCallback((e: MouseEvent) => { - target.current = e.currentTarget as HTMLElement; + if (!(e.currentTarget instanceof HTMLElement)) { + return; + } + target.current = e.currentTarget; initialMousePosition.current = {x: e.clientX, y: e.clientY}; }, []); diff --git a/src/components/Tooltip/PopoverAnchorTooltip.tsx b/src/components/Tooltip/PopoverAnchorTooltip.tsx index df65f8752bf4..e524c55f6d2f 100644 --- a/src/components/Tooltip/PopoverAnchorTooltip.tsx +++ b/src/components/Tooltip/PopoverAnchorTooltip.tsx @@ -10,7 +10,7 @@ function PopoverAnchorTooltip({shouldRender = true, children, ...props}: Tooltip const isPopoverRelatedToTooltipOpen = useMemo(() => { // eslint-disable-next-line @typescript-eslint/dot-notation - const tooltipNode = tooltipRef.current ? tooltipRef.current['_childNode'] : null; + const tooltipNode = tooltipRef.current?.['_childNode'] ?? null; if (isOpen && popover?.anchorRef?.current && tooltipNode && (tooltipNode.contains(popover.anchorRef.current) || tooltipNode === popover.anchorRef.current)) { return true; } diff --git a/src/types/utils/callOrReturn.ts b/src/types/utils/callOrReturn.ts index 985a752ad68c..d51226544962 100644 --- a/src/types/utils/callOrReturn.ts +++ b/src/types/utils/callOrReturn.ts @@ -1,5 +1,9 @@ function callOrReturn(value: TValue | (() => TValue)): TValue { - return typeof value === 'function' ? (value as () => TValue)() : value; + if (typeof value === 'function') { + return (value as () => TValue)(); + } + + return value; } export default callOrReturn; From 2e46aa96e76a8caca8494edbba032feebcfc3f6a Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Mon, 27 Nov 2023 14:36:50 +0100 Subject: [PATCH 17/26] rename file --- .../{BaseTooltip.native.tsx => BaseTooltip/index.native.tsx} | 0 src/components/Tooltip/{BaseTooltip.tsx => BaseTooltip/index.tsx} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename src/components/Tooltip/{BaseTooltip.native.tsx => BaseTooltip/index.native.tsx} (100%) rename src/components/Tooltip/{BaseTooltip.tsx => BaseTooltip/index.tsx} (100%) diff --git a/src/components/Tooltip/BaseTooltip.native.tsx b/src/components/Tooltip/BaseTooltip/index.native.tsx similarity index 100% rename from src/components/Tooltip/BaseTooltip.native.tsx rename to src/components/Tooltip/BaseTooltip/index.native.tsx diff --git a/src/components/Tooltip/BaseTooltip.tsx b/src/components/Tooltip/BaseTooltip/index.tsx similarity index 100% rename from src/components/Tooltip/BaseTooltip.tsx rename to src/components/Tooltip/BaseTooltip/index.tsx From 99eaf182e7c77e1cfb8c6645f211ee331729e2c1 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Mon, 27 Nov 2023 14:37:57 +0100 Subject: [PATCH 18/26] cleanup --- src/components/Tooltip/BaseTooltip/index.native.tsx | 4 ++-- src/components/Tooltip/BaseTooltip/index.tsx | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/Tooltip/BaseTooltip/index.native.tsx b/src/components/Tooltip/BaseTooltip/index.native.tsx index 6d3938940fd8..d52ccb9bca79 100644 --- a/src/components/Tooltip/BaseTooltip/index.native.tsx +++ b/src/components/Tooltip/BaseTooltip/index.native.tsx @@ -1,7 +1,7 @@ // We can't use the common component for the Tooltip as Web implementation uses DOM specific method to -import ChildrenProps from '@src/types/utils/ChildrenProps'; +import TooltipProps from '@components/Tooltip/types'; -function Tooltip({children}: ChildrenProps) { +function Tooltip({children}: TooltipProps) { return children; } diff --git a/src/components/Tooltip/BaseTooltip/index.tsx b/src/components/Tooltip/BaseTooltip/index.tsx index 896f250c8b8b..a492b4b871e6 100644 --- a/src/components/Tooltip/BaseTooltip/index.tsx +++ b/src/components/Tooltip/BaseTooltip/index.tsx @@ -10,9 +10,9 @@ import variables from '@styles/variables'; import CONST from '@src/CONST'; import StringUtils from '@src/libs/StringUtils'; import callOrReturn from '@src/types/utils/callOrReturn'; -import TooltipRenderedOnPageBody from './TooltipRenderedOnPageBody'; -import TooltipSense from './TooltipSense'; -import TooltipProps from './types'; +import TooltipRenderedOnPageBody from '@components/Tooltip/TooltipRenderedOnPageBody'; +import TooltipSense from '@components/Tooltip/TooltipSense'; +import TooltipProps from '@components/Tooltip/types'; const hasHoverSupport = DeviceCapabilities.hasHoverSupport(); From 8b73221bb4e159b84139680d76d496cd2382d4e2 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Mon, 27 Nov 2023 14:41:14 +0100 Subject: [PATCH 19/26] make props optional --- src/components/Tooltip/TooltipRenderedOnPageBody.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Tooltip/TooltipRenderedOnPageBody.tsx b/src/components/Tooltip/TooltipRenderedOnPageBody.tsx index 816df53f2848..ea79e013666a 100644 --- a/src/components/Tooltip/TooltipRenderedOnPageBody.tsx +++ b/src/components/Tooltip/TooltipRenderedOnPageBody.tsx @@ -29,11 +29,11 @@ type TooltipRenderedOnPageBodyProps = { /** Any additional amount to manually adjust the horizontal position of the tooltip. A positive value shifts the tooltip to the right, and a negative value shifts it to the left. */ - shiftHorizontal: number; + shiftHorizontal?: number; /** Any additional amount to manually adjust the vertical position of the tooltip. A positive value shifts the tooltip down, and a negative value shifts it up. */ - shiftVertical: number; + shiftVertical?: number; } & Pick; // Props will change frequently. From 46268acb27b1856b8e657083054430d4cdadbf82 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Mon, 27 Nov 2023 14:49:01 +0100 Subject: [PATCH 20/26] prettier --- src/components/Tooltip/BaseTooltip/index.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/Tooltip/BaseTooltip/index.tsx b/src/components/Tooltip/BaseTooltip/index.tsx index a492b4b871e6..e2a2c46e4546 100644 --- a/src/components/Tooltip/BaseTooltip/index.tsx +++ b/src/components/Tooltip/BaseTooltip/index.tsx @@ -2,6 +2,9 @@ import {BoundsObserver} from '@react-ng/bounds-observer'; import React, {ForwardedRef, forwardRef, memo, useCallback, useEffect, useRef, useState} from 'react'; import {Animated} from 'react-native'; import Hoverable from '@components/Hoverable'; +import TooltipRenderedOnPageBody from '@components/Tooltip/TooltipRenderedOnPageBody'; +import TooltipSense from '@components/Tooltip/TooltipSense'; +import TooltipProps from '@components/Tooltip/types'; import useLocalize from '@hooks/useLocalize'; import usePrevious from '@hooks/usePrevious'; import useWindowDimensions from '@hooks/useWindowDimensions'; @@ -10,9 +13,6 @@ import variables from '@styles/variables'; import CONST from '@src/CONST'; import StringUtils from '@src/libs/StringUtils'; import callOrReturn from '@src/types/utils/callOrReturn'; -import TooltipRenderedOnPageBody from '@components/Tooltip/TooltipRenderedOnPageBody'; -import TooltipSense from '@components/Tooltip/TooltipSense'; -import TooltipProps from '@components/Tooltip/types'; const hasHoverSupport = DeviceCapabilities.hasHoverSupport(); From cf500553f96e027cabace609a73d143daba130e3 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Mon, 4 Dec 2023 09:56:37 +0100 Subject: [PATCH 21/26] finalize merge --- src/components/Tooltip/BaseTooltip/index.tsx | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/components/Tooltip/BaseTooltip/index.tsx b/src/components/Tooltip/BaseTooltip/index.tsx index bf1110284b61..95c9533fff50 100644 --- a/src/components/Tooltip/BaseTooltip/index.tsx +++ b/src/components/Tooltip/BaseTooltip/index.tsx @@ -185,11 +185,9 @@ function Tooltip( }, []); const updateTargetPositionOnMouseEnter = useCallback( - (e) => { + (e: MouseEvent) => { updateTargetAndMousePosition(e); - if (children.props.onMouseEnter) { - children.props.onMouseEnter(e); - } + children.props.onMouseEnter?.(e); }, [children.props, updateTargetAndMousePosition], ); @@ -227,7 +225,6 @@ function Tooltip( ref={ref} > Date: Mon, 4 Dec 2023 10:04:51 +0100 Subject: [PATCH 22/26] prettier --- src/components/Tooltip/TooltipRenderedOnPageBody.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Tooltip/TooltipRenderedOnPageBody.tsx b/src/components/Tooltip/TooltipRenderedOnPageBody.tsx index ca6439befda7..ef3433a3d69f 100644 --- a/src/components/Tooltip/TooltipRenderedOnPageBody.tsx +++ b/src/components/Tooltip/TooltipRenderedOnPageBody.tsx @@ -4,10 +4,10 @@ import {Animated, View} from 'react-native'; import Text from '@components/Text'; import Log from '@libs/Log'; import getTooltipStyles from '@styles/getTooltipStyles'; -import textRef from '@src/types/utils/textRef'; -import viewRef from '@src/types/utils/viewRef'; import useTheme from '@styles/themes/useTheme'; import useThemeStyles from '@styles/useThemeStyles'; +import textRef from '@src/types/utils/textRef'; +import viewRef from '@src/types/utils/viewRef'; import TooltipProps from './types'; type TooltipRenderedOnPageBodyProps = { From 1348cb8cb9e106b516b2e29d2f01e3a616917b04 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Wed, 6 Dec 2023 09:54:48 +0100 Subject: [PATCH 23/26] address review --- src/components/Tooltip/TooltipRenderedOnPageBody.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Tooltip/TooltipRenderedOnPageBody.tsx b/src/components/Tooltip/TooltipRenderedOnPageBody.tsx index ef3433a3d69f..01b219226407 100644 --- a/src/components/Tooltip/TooltipRenderedOnPageBody.tsx +++ b/src/components/Tooltip/TooltipRenderedOnPageBody.tsx @@ -60,9 +60,9 @@ function TooltipRenderedOnPageBody({ // The width of tooltip's inner content. Has to be undefined in the beginning // as a width of 0 will cause the content to be rendered of a width of 0, // which prevents us from measuring it correctly. - const [contentMeasuredWidth, setContentMeasuredWidth] = useState(undefined); + const [contentMeasuredWidth, setContentMeasuredWidth] = useState(); // The height of tooltip's wrapper. - const [wrapperMeasuredHeight, setWrapperMeasuredHeight] = useState(undefined); + const [wrapperMeasuredHeight, setWrapperMeasuredHeight] = useState(); const contentRef = useRef(null); const rootWrapper = useRef(null); From 461a4282b03e3d2186509aa8ac236630d3496dde Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Wed, 6 Dec 2023 10:07:17 +0100 Subject: [PATCH 24/26] remove forward ref --- src/components/Tooltip/BaseTooltip/index.tsx | 32 +++++++++---------- .../Tooltip/PopoverAnchorTooltip.tsx | 2 +- src/components/Tooltip/types.ts | 6 +++- 3 files changed, 21 insertions(+), 19 deletions(-) diff --git a/src/components/Tooltip/BaseTooltip/index.tsx b/src/components/Tooltip/BaseTooltip/index.tsx index e2a2c46e4546..6530d81da6dc 100644 --- a/src/components/Tooltip/BaseTooltip/index.tsx +++ b/src/components/Tooltip/BaseTooltip/index.tsx @@ -1,5 +1,5 @@ import {BoundsObserver} from '@react-ng/bounds-observer'; -import React, {ForwardedRef, forwardRef, memo, useCallback, useEffect, useRef, useState} from 'react'; +import React, {memo, useCallback, useEffect, useRef, useState} from 'react'; import {Animated} from 'react-native'; import Hoverable from '@components/Hoverable'; import TooltipRenderedOnPageBody from '@components/Tooltip/TooltipRenderedOnPageBody'; @@ -55,20 +55,18 @@ function chooseBoundingBox(target: HTMLElement, clientX: number, clientY: number return target.getBoundingClientRect(); } -function Tooltip( - { - children, - numberOfLines = CONST.TOOLTIP_MAX_LINES, - maxWidth = variables.sideBarWidth, - text = '', - renderTooltipContent, - renderTooltipContentKey = [], - shouldHandleScroll = false, - shiftHorizontal = 0, - shiftVertical = 0, - }: TooltipProps, - ref: ForwardedRef, -) { +function Tooltip({ + children, + numberOfLines = CONST.TOOLTIP_MAX_LINES, + maxWidth = variables.sideBarWidth, + text = '', + renderTooltipContent, + renderTooltipContentKey = [], + shouldHandleScroll = false, + shiftHorizontal = 0, + shiftVertical = 0, + tooltipRef, +}: TooltipProps) { const {preferredLocale} = useLocalize(); const {windowWidth} = useWindowDimensions(); @@ -214,7 +212,7 @@ function Tooltip( {children} diff --git a/src/components/Tooltip/types.ts b/src/components/Tooltip/types.ts index df95c3ea5979..6388c14741ab 100644 --- a/src/components/Tooltip/types.ts +++ b/src/components/Tooltip/types.ts @@ -1,4 +1,5 @@ -import {ReactElement, ReactNode} from 'react'; +import {BoundsObserver} from '@react-ng/bounds-observer'; +import {ReactElement, ReactNode, RefObject} from 'react'; type TooltipProps = { /** The text to display in the tooltip. If text is ommitted, only children will be rendered. */ @@ -29,6 +30,9 @@ type TooltipProps = { /** passes this down to Hoverable component to decide whether to handle the scroll behaviour to show hover once the scroll ends */ shouldHandleScroll?: boolean; + + /** Reference to the tooltip container */ + tooltipRef?: RefObject | null; }; type TooltipExtendedProps = TooltipProps & { From db258e30d5e9160198b739ec7eedeccfe2ea90ca Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Wed, 6 Dec 2023 10:17:53 +0100 Subject: [PATCH 25/26] Revert "remove forward ref" This reverts commit 461a4282b03e3d2186509aa8ac236630d3496dde. --- src/components/Tooltip/BaseTooltip/index.tsx | 32 ++++++++++--------- .../Tooltip/PopoverAnchorTooltip.tsx | 2 +- src/components/Tooltip/types.ts | 6 +--- 3 files changed, 19 insertions(+), 21 deletions(-) diff --git a/src/components/Tooltip/BaseTooltip/index.tsx b/src/components/Tooltip/BaseTooltip/index.tsx index 6530d81da6dc..e2a2c46e4546 100644 --- a/src/components/Tooltip/BaseTooltip/index.tsx +++ b/src/components/Tooltip/BaseTooltip/index.tsx @@ -1,5 +1,5 @@ import {BoundsObserver} from '@react-ng/bounds-observer'; -import React, {memo, useCallback, useEffect, useRef, useState} from 'react'; +import React, {ForwardedRef, forwardRef, memo, useCallback, useEffect, useRef, useState} from 'react'; import {Animated} from 'react-native'; import Hoverable from '@components/Hoverable'; import TooltipRenderedOnPageBody from '@components/Tooltip/TooltipRenderedOnPageBody'; @@ -55,18 +55,20 @@ function chooseBoundingBox(target: HTMLElement, clientX: number, clientY: number return target.getBoundingClientRect(); } -function Tooltip({ - children, - numberOfLines = CONST.TOOLTIP_MAX_LINES, - maxWidth = variables.sideBarWidth, - text = '', - renderTooltipContent, - renderTooltipContentKey = [], - shouldHandleScroll = false, - shiftHorizontal = 0, - shiftVertical = 0, - tooltipRef, -}: TooltipProps) { +function Tooltip( + { + children, + numberOfLines = CONST.TOOLTIP_MAX_LINES, + maxWidth = variables.sideBarWidth, + text = '', + renderTooltipContent, + renderTooltipContentKey = [], + shouldHandleScroll = false, + shiftHorizontal = 0, + shiftVertical = 0, + }: TooltipProps, + ref: ForwardedRef, +) { const {preferredLocale} = useLocalize(); const {windowWidth} = useWindowDimensions(); @@ -212,7 +214,7 @@ function Tooltip({ {children} diff --git a/src/components/Tooltip/types.ts b/src/components/Tooltip/types.ts index 6388c14741ab..df95c3ea5979 100644 --- a/src/components/Tooltip/types.ts +++ b/src/components/Tooltip/types.ts @@ -1,5 +1,4 @@ -import {BoundsObserver} from '@react-ng/bounds-observer'; -import {ReactElement, ReactNode, RefObject} from 'react'; +import {ReactElement, ReactNode} from 'react'; type TooltipProps = { /** The text to display in the tooltip. If text is ommitted, only children will be rendered. */ @@ -30,9 +29,6 @@ type TooltipProps = { /** passes this down to Hoverable component to decide whether to handle the scroll behaviour to show hover once the scroll ends */ shouldHandleScroll?: boolean; - - /** Reference to the tooltip container */ - tooltipRef?: RefObject | null; }; type TooltipExtendedProps = TooltipProps & { From 203cd81b0dd64b1d81661590c4058d4ff66b8c8c Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel <104823336+kosmydel@users.noreply.github.com> Date: Wed, 6 Dec 2023 10:40:49 +0100 Subject: [PATCH 26/26] fix warning on native --- src/components/Tooltip/BaseTooltip/index.native.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/components/Tooltip/BaseTooltip/index.native.tsx b/src/components/Tooltip/BaseTooltip/index.native.tsx index d52ccb9bca79..6d9eb24aaec5 100644 --- a/src/components/Tooltip/BaseTooltip/index.native.tsx +++ b/src/components/Tooltip/BaseTooltip/index.native.tsx @@ -1,10 +1,12 @@ -// We can't use the common component for the Tooltip as Web implementation uses DOM specific method to -import TooltipProps from '@components/Tooltip/types'; +import {forwardRef} from 'react'; +import ChildrenProps from '@src/types/utils/ChildrenProps'; -function Tooltip({children}: TooltipProps) { +// We can't use the common component for the Tooltip as Web implementation uses DOM specific method +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function Tooltip({children}: ChildrenProps, ref: unknown) { return children; } Tooltip.displayName = 'Tooltip'; -export default Tooltip; +export default forwardRef(Tooltip);