Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TS migration] Migrate 'Tooltip' component to TypeScript #31544

Merged
merged 31 commits into from
Dec 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
d75347f
migrate Tooltip to TS
kosmydel Nov 20, 2023
6e99530
fixes
kosmydel Nov 20, 2023
9296400
more fixes
kosmydel Nov 20, 2023
8a97ea0
fix refs
kosmydel Nov 20, 2023
15d3837
remove unknown type
kosmydel Nov 20, 2023
0f81bb0
refactor
kosmydel Nov 20, 2023
9248025
Change order of props in Tooltip
kosmydel Nov 20, 2023
3f1ba1f
fix
kosmydel Nov 20, 2023
88f20f3
minor changes
kosmydel Nov 20, 2023
9c65d98
reuse some types
kosmydel Nov 20, 2023
542a6d3
fix
kosmydel Nov 20, 2023
316fc40
Add textRef util
blazejkustra Nov 23, 2023
b401d93
use children props
kosmydel Nov 27, 2023
ac0d976
address review
kosmydel Nov 27, 2023
cc80269
use forwardRef
kosmydel Nov 27, 2023
bc65c46
address review
kosmydel Nov 27, 2023
2e46aa9
rename file
kosmydel Nov 27, 2023
99eaf18
cleanup
kosmydel Nov 27, 2023
8b73221
make props optional
kosmydel Nov 27, 2023
46268ac
prettier
kosmydel Nov 27, 2023
802a5a6
Merge branch 'main' into @kosmydel/ts/tooltip
kosmydel Nov 28, 2023
e971fa0
Merge branch 'main' into @kosmydel/ts/tooltip
kosmydel Dec 4, 2023
cf50055
finalize merge
kosmydel Dec 4, 2023
8eb0c72
prettier
kosmydel Dec 4, 2023
ffbc724
Merge branch 'main' into @kosmydel/ts/tooltip
kosmydel Dec 6, 2023
1348cb8
address review
kosmydel Dec 6, 2023
461a428
remove forward ref
kosmydel Dec 6, 2023
db258e3
Revert "remove forward ref"
kosmydel Dec 6, 2023
203cd81
fix warning on native
kosmydel Dec 6, 2023
d1380a5
Merge branch 'main' into @kosmydel/ts/tooltip
kosmydel Dec 7, 2023
629907f
Merge branch 'main' into @kosmydel/ts/tooltip
kosmydel Dec 8, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion src/components/Pressable/PressableWithDelayToggle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,6 @@ function PressableWithDelayToggle(
<>
{inline && labelText}
<Tooltip
containerStyles={[styles.flexRow]}
kosmydel marked this conversation as resolved.
Show resolved Hide resolved
text={tooltipTexts}
shouldRender
>
Expand Down
21 changes: 0 additions & 21 deletions src/components/Tooltip/BaseTooltip.native.js

This file was deleted.

12 changes: 12 additions & 0 deletions src/components/Tooltip/BaseTooltip/index.native.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import {forwardRef} from 'react';
import ChildrenProps from '@src/types/utils/ChildrenProps';

// 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 forwardRef(Tooltip);
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import {BoundsObserver} from '@react-ng/bounds-observer';
import Str from 'expensify-common/lib/str';
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 _ from 'underscore';
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';
import * as DeviceCapabilities from '@libs/DeviceCapabilities';
import * as tooltipPropTypes from './tooltipPropTypes';
import TooltipRenderedOnPageBody from './TooltipRenderedOnPageBody';
import TooltipSense from './TooltipSense';
import variables from '@styles/variables';
import CONST from '@src/CONST';
import StringUtils from '@src/libs/StringUtils';
import callOrReturn from '@src/types/utils/callOrReturn';

const hasHoverSupport = DeviceCapabilities.hasHoverSupport();

Expand All @@ -33,14 +35,15 @@ 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;
const clientXMax = clientX + slop;
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) {
Expand All @@ -52,7 +55,20 @@ function chooseBoundingBox(target, clientX, clientY) {
return target.getBoundingClientRect();
}

function Tooltip({children, numberOfLines, maxWidth, text, renderTooltipContent, renderTooltipContentKey, shouldHandleScroll, shiftHorizontal, shiftVertical, tooltipRef}) {
function Tooltip(
{
children,
numberOfLines = CONST.TOOLTIP_MAX_LINES,
maxWidth = variables.sideBarWidth,
text = '',
renderTooltipContent,
renderTooltipContentKey = [],
shouldHandleScroll = false,
shiftHorizontal = 0,
shiftVertical = 0,
}: TooltipProps,
ref: ForwardedRef<BoundsObserver>,
) {
const {preferredLocale} = useLocalize();
const {windowWidth} = useWindowDimensions();

Expand All @@ -74,10 +90,13 @@ function Tooltip({children, numberOfLines, maxWidth, text, renderTooltipContent,
const isAnimationCanceled = useRef(false);
const prevText = usePrevious(text);

const target = useRef(null);
const target = useRef<HTMLElement | null>(null);
kosmydel marked this conversation as resolved.
Show resolved Hide resolved
const initialMousePosition = useRef({x: 0, y: 0});

const updateTargetAndMousePosition = useCallback((e) => {
const updateTargetAndMousePosition = useCallback((e: MouseEvent) => {
if (!(e.currentTarget instanceof HTMLElement)) {
return;
}
target.current = e.currentTarget;
initialMousePosition.current = {x: e.clientX, y: e.clientY};
}, []);
Expand Down Expand Up @@ -120,10 +139,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);
}
Expand Down Expand Up @@ -169,7 +186,7 @@ function Tooltip({children, numberOfLines, maxWidth, text, renderTooltipContent,

// 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;
}

Expand All @@ -183,21 +200,21 @@ function Tooltip({children, numberOfLines, maxWidth, text, renderTooltipContent,
yOffset={yOffset}
targetWidth={wrapperWidth}
targetHeight={wrapperHeight}
shiftHorizontal={Str.result(shiftHorizontal)}
shiftVertical={Str.result(shiftVertical)}
shiftHorizontal={callOrReturn(shiftHorizontal)}
shiftVertical={callOrReturn(shiftVertical)}
kosmydel marked this conversation as resolved.
Show resolved Hide resolved
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('-')}
kosmydel marked this conversation as resolved.
Show resolved Hide resolved
/>
)}
<BoundsObserver
enabled={isVisible}
onBoundsChange={updateBounds}
ref={tooltipRef}
ref={ref}
>
<Hoverable
onMouseEnter={updateTargetAndMousePosition}
Expand All @@ -212,8 +229,6 @@ function Tooltip({children, numberOfLines, maxWidth, text, renderTooltipContent,
);
}

Tooltip.propTypes = tooltipPropTypes.propTypes;
Tooltip.defaultProps = tooltipPropTypes.defaultProps;
Tooltip.displayName = 'Tooltip';

export default memo(Tooltip);
export default memo(forwardRef(Tooltip));
59 changes: 0 additions & 59 deletions src/components/Tooltip/PopoverAnchorTooltip.js

This file was deleted.

38 changes: 38 additions & 0 deletions src/components/Tooltip/PopoverAnchorTooltip.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import {BoundsObserver} from '@react-ng/bounds-observer';
import React, {useContext, useMemo, useRef} from 'react';
import {PopoverContext} from '@components/PopoverProvider';
import BaseTooltip from './BaseTooltip';
import {TooltipExtendedProps} from './types';

function PopoverAnchorTooltip({shouldRender = true, children, ...props}: TooltipExtendedProps) {
const {isOpen, popover} = useContext(PopoverContext);
const tooltipRef = useRef<BoundsObserver>(null);

const isPopoverRelatedToTooltipOpen = useMemo(() => {
// eslint-disable-next-line @typescript-eslint/dot-notation
const tooltipNode = tooltipRef.current?.['_childNode'] ?? null;
if (isOpen && popover?.anchorRef?.current && tooltipNode && (tooltipNode.contains(popover.anchorRef.current) || tooltipNode === popover.anchorRef.current)) {
return true;
}

return false;
}, [isOpen, popover]);

if (!shouldRender || isPopoverRelatedToTooltipOpen) {
return children;
}

return (
<BaseTooltip
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
ref={tooltipRef}
>
{children}
</BaseTooltip>
);
}

PopoverAnchorTooltip.displayName = 'PopoverAnchorTooltip';

export default PopoverAnchorTooltip;
Loading
Loading