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

Feat: Make it possible to click the element the product training tooltip is pointing to #54710

Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
6174a6d
Make it possible to click the element the product training tooltip is…
rayane-djouah Dec 17, 2024
ed54b83
use isScreenFocused
rayane-djouah Dec 17, 2024
2d2e16d
Merge branch 'main' into Make-it-possible-to-click-the-element-the-pr…
rayane-djouah Dec 27, 2024
0d49f67
fix bug
rayane-djouah Dec 27, 2024
fe4bcb6
fix bug
rayane-djouah Dec 27, 2024
8de62b1
remove shouldHideEducationalTooltip
rayane-djouah Dec 27, 2024
4c13d72
QAB hideProductTrainingTooltip
rayane-djouah Dec 27, 2024
9d578d3
QAB hideProductTrainingTooltip
rayane-djouah Dec 27, 2024
629c8c2
remove console log
rayane-djouah Dec 27, 2024
5e5b74b
Merge branch 'main' into Make-it-possible-to-click-the-element-the-pr…
rayane-djouah Jan 1, 2025
57811e3
Merge branch 'main' into Make-it-possible-to-click-the-element-the-pr…
rayane-djouah Jan 2, 2025
c8dc967
fix errors
rayane-djouah Jan 2, 2025
b7d5186
correct copy usage
rayane-djouah Jan 2, 2025
2042262
Refactor
rayane-djouah Jan 3, 2025
06f1b75
hideProductTrainingTooltip for RENAME_SAVED_SEARCH tooltip
rayane-djouah Jan 3, 2025
4b2a934
revert a change
rayane-djouah Jan 3, 2025
fcaf530
Fix SEARCH_FILTER_BUTTON_TOOLTIP for narrow layout
rayane-djouah Jan 3, 2025
b8f68ba
Merge branch 'Expensify:main' into Make-it-possible-to-click-the-elem…
rayane-djouah Jan 3, 2025
1992248
Fix: tooltip does not disappear when navigating away for the first time
rayane-djouah Jan 3, 2025
b47997b
Fix bug: when a migrated user logged in and dismiss modal user dont s…
rayane-djouah Jan 3, 2025
127c7f8
Fix lint errors
rayane-djouah Jan 3, 2025
2737c55
add mocks for useNavigationState
rayane-djouah Jan 3, 2025
51eca4a
create useIsCurrentRouteHome hook
rayane-djouah Jan 3, 2025
163bf6a
add useIsCurrentRouteHome mock
rayane-djouah Jan 3, 2025
46da42e
fix ESlint errors
rayane-djouah Jan 3, 2025
0f88b45
Fix: Tooltips not showing on native
rayane-djouah Jan 4, 2025
e8a3b75
Fix: Hide the extra "More" tooltip when we have an educational toolti…
rayane-djouah Jan 4, 2025
b33e69f
Refactor
rayane-djouah Jan 4, 2025
aa2b173
add comment
rayane-djouah Jan 4, 2025
473cad5
Fix bug
rayane-djouah Jan 5, 2025
b4187ee
Fix QAB tooltip does not hide immediately
rayane-djouah Jan 5, 2025
d11826c
Merge branch 'Expensify:main' into Make-it-possible-to-click-the-elem…
rayane-djouah Jan 5, 2025
1a52149
Fix: bottomtab tooltips (inbox and FAB) only hides on navigation to s…
rayane-djouah Jan 6, 2025
ed68330
move prop to most relevant type
rayane-djouah Jan 6, 2025
270a302
Fix: clicking on admin chat dismiss the workspacechat tooltip
rayane-djouah Jan 6, 2025
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
3 changes: 1 addition & 2 deletions src/components/FloatingActionButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ function FloatingActionButton({onPress, isActive, accessibilityLabel, role}: Flo
);

const toggleFabAction = (event: GestureResponderEvent | KeyboardEvent | undefined) => {
hideProductTrainingTooltip();
// Drop focus to avoid blue focus ring.
fabPressable.current?.blur();
onPress(event);
Expand All @@ -120,11 +121,9 @@ function FloatingActionButton({onPress, isActive, accessibilityLabel, role}: Flo
horizontal: isNarrowScreenOnWeb ? CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.CENTER : CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.RIGHT,
vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.BOTTOM,
}}
shouldUseOverlay
shiftHorizontal={isNarrowScreenOnWeb ? 0 : variables.fabTooltipShiftHorizontal}
renderTooltipContent={renderProductTrainingTooltip}
wrapperStyle={styles.productTrainingTooltipWrapper}
onHideTooltip={hideProductTrainingTooltip}
>
<PressableWithoutFeedback
ref={(el) => {
Expand Down
25 changes: 12 additions & 13 deletions src/components/LHNOptionsList/OptionRowLHN.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {useFocusEffect} from '@react-navigation/native';
import React, {useCallback, useMemo, useRef, useState} from 'react';
import React, {useCallback, useRef, useState} from 'react';
import type {GestureResponderEvent, ViewStyle} from 'react-native';
import {StyleSheet, View} from 'react-native';
import {useOnyx} from 'react-native-onyx';
Expand Down Expand Up @@ -44,9 +44,12 @@
const StyleUtils = useStyleUtils();
const [isScreenFocused, setIsScreenFocused] = useState(false);
const {shouldUseNarrowLayout} = useResponsiveLayout();
const [modal] = useOnyx(ONYXKEYS.MODAL);
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
const isModalVisible = modal?.isVisible || modal?.willAlertModalBecomeVisible;

// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${optionItem?.reportID || -1}`);

Check failure on line 52 in src/components/LHNOptionsList/OptionRowLHN.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID);
const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED);
const session = useSession();
Expand All @@ -54,16 +57,7 @@
const isOnboardingGuideAssigned = introSelected?.choice === CONST.ONBOARDING_CHOICES.MANAGE_TEAM && !session?.email?.includes('+');
const shouldShowGetStartedTooltip = isOnboardingGuideAssigned ? ReportUtils.isAdminRoom(report) : ReportUtils.isConciergeChatReport(report);

const {tooltipToRender, shouldShowTooltip} = useMemo(() => {
const tooltip = shouldShowGetStartedTooltip ? CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.CONCEIRGE_LHN_GBR : CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.LHN_WORKSPACE_CHAT_TOOLTIP;

// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
return {tooltipToRender: tooltip, shouldShowTooltip: shouldUseNarrowLayout ? isScreenFocused : true};
}, [shouldShowGetStartedTooltip, isScreenFocused, shouldUseNarrowLayout]);

const {shouldShowProductTrainingTooltip, renderProductTrainingTooltip, hideProductTrainingTooltip} = useProductTrainingContext(tooltipToRender, shouldShowTooltip);
const {translate} = useLocalize();
const [isContextMenuActive, setIsContextMenuActive] = useState(false);
const tooltipToRender = shouldShowGetStartedTooltip ? CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.CONCEIRGE_LHN_GBR : CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.LHN_WORKSPACE_CHAT_TOOLTIP;

useFocusEffect(
useCallback(() => {
Expand All @@ -74,6 +68,12 @@
}, []),
);

const shouldShowTooltip = shouldUseNarrowLayout ? isScreenFocused && !isModalVisible : !isModalVisible;

const {shouldShowProductTrainingTooltip, renderProductTrainingTooltip, hideProductTrainingTooltip} = useProductTrainingContext(tooltipToRender, shouldShowTooltip);
const {translate} = useLocalize();
const [isContextMenuActive, setIsContextMenuActive] = useState(false);

const isInFocusMode = viewMode === CONST.OPTION_MODE.COMPACT;
const sidebarInnerRowStyle = StyleSheet.flatten<ViewStyle>(
isInFocusMode
Expand Down Expand Up @@ -165,10 +165,8 @@
horizontal: isActiveWorkspaceChat ? CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.LEFT : CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.RIGHT,
vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.TOP,
}}
shouldUseOverlay
shiftHorizontal={isActiveWorkspaceChat ? variables.workspaceLHNtooltipShiftHorizontal : variables.gbrTooltipShiftHorizontal}
shiftVertical={isActiveWorkspaceChat ? 0 : variables.composerTooltipShiftVertical}
onHideTooltip={hideProductTrainingTooltip}
wrapperStyle={styles.productTrainingTooltipWrapper}
>
<View>
Expand All @@ -183,6 +181,7 @@
event?.preventDefault();
// Enable Composer to focus on clicking the same chat after opening the context menu.
ReportActionComposeFocusManager.focus();
hideProductTrainingTooltip();
onSelectRow(optionItem, popoverAnchor);
}}
onMouseDown={(event) => {
Expand Down
6 changes: 0 additions & 6 deletions src/components/MenuItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -340,9 +340,6 @@ type MenuItemBaseProps = {
/** Should selected item be marked with checkmark */
shouldShowSelectedItemCheck?: boolean;

/** Handles what to do when hiding the tooltip */
onHideTooltip?: () => void;

/** Should use auto width for the icon container. */
shouldIconUseAutoWidthStyle?: boolean;

Expand Down Expand Up @@ -461,7 +458,6 @@ function MenuItem(
renderTooltipContent,
additionalIconStyles,
shouldShowSelectedItemCheck = false,
onHideTooltip,
shouldIconUseAutoWidthStyle = false,
shouldBreakWord = false,
pressableTestID,
Expand Down Expand Up @@ -600,8 +596,6 @@ function MenuItem(
wrapperStyle={tooltipWrapperStyle}
shiftHorizontal={tooltipShiftHorizontal}
shiftVertical={tooltipShiftVertical}
shouldAutoDismiss
onHideTooltip={onHideTooltip}
>
<View>
<Hoverable>
Expand Down
5 changes: 3 additions & 2 deletions src/components/ProductTrainingContext/TOOLTIPS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,9 @@ const TOOLTIPS: Record<ProductTrainingTooltipName, TooltipData> = {
},
[WORKSAPCE_CHAT_CREATE]: {
content: [
{text: 'productTrainingTooltip.workspaceChatCreate.part1', isBold: true},
{text: 'productTrainingTooltip.workspaceChatCreate.part2', isBold: false},
{text: 'productTrainingTooltip.workspaceChatCreate.part1', isBold: false},
{text: 'productTrainingTooltip.workspaceChatCreate.part2', isBold: true},
{text: 'productTrainingTooltip.workspaceChatCreate.part3', isBold: false},
],
onHideTooltip: () => dismissProductTraining(WORKSAPCE_CHAT_CREATE),
name: WORKSAPCE_CHAT_CREATE,
Expand Down
12 changes: 7 additions & 5 deletions src/components/ProductTrainingContext/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -156,11 +156,10 @@ const useProductTrainingContext = (tooltipName: ProductTrainingTooltipName, shou
useEffect(() => {
if (shouldShow) {
registerTooltip(tooltipName);
return () => {
unregisterTooltip(tooltipName);
};
}
return () => {};
return () => {
unregisterTooltip(tooltipName);
};
}, [tooltipName, registerTooltip, unregisterTooltip, shouldShow]);

const renderProductTrainingTooltip = useCallback(() => {
Expand Down Expand Up @@ -209,10 +208,13 @@ const useProductTrainingContext = (tooltipName: ProductTrainingTooltipName, shou
}, [shouldRenderTooltip, tooltipName, shouldShow]);

const hideProductTrainingTooltip = useCallback(() => {
if (!shouldShowProductTrainingTooltip) {
return;
}
const tooltip = TOOLTIPS[tooltipName];
tooltip.onHideTooltip();
unregisterTooltip(tooltipName);
}, [tooltipName, unregisterTooltip]);
}, [tooltipName, shouldShowProductTrainingTooltip, unregisterTooltip]);

return {
renderProductTrainingTooltip,
Expand Down
31 changes: 22 additions & 9 deletions src/components/Search/SearchPageHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {useIsFocused} from '@react-navigation/native';
import React, {useMemo, useState} from 'react';
import {useFocusEffect} from '@react-navigation/native';
import React, {useCallback, useMemo, useState} from 'react';
import {InteractionManager, View} from 'react-native';
import {useOnyx} from 'react-native-onyx';
import Button from '@components/Button';
Expand Down Expand Up @@ -58,14 +58,23 @@
const [isDeleteExpensesConfirmModalVisible, setIsDeleteExpensesConfirmModalVisible] = useState(false);
const [isOfflineModalVisible, setIsOfflineModalVisible] = useState(false);
const [isDownloadErrorModalVisible, setIsDownloadErrorModalVisible] = useState(false);
const isFocused = useIsFocused();
const {renderProductTrainingTooltip, shouldShowProductTrainingTooltip, hideProductTrainingTooltip} = useProductTrainingContext(
CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.SEARCH_FILTER_BUTTON_TOOLTIP,
isFocused,
);
const [modal] = useOnyx(ONYXKEYS.MODAL);
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
const isModalVisible = modal?.isVisible || modal?.willAlertModalBecomeVisible;

const [isScreenFocused, setIsScreenFocused] = useState(false);

const {status, hash} = queryJSON;

useFocusEffect(
useCallback(() => {
setIsScreenFocused(true);
return () => {
setIsScreenFocused(false);
};
}, []),
);

const selectedTransactionsKeys = Object.keys(selectedTransactions ?? {});

const handleDeleteExpenses = () => {
Expand Down Expand Up @@ -189,7 +198,7 @@

const reportIDList = selectedReports?.filter((report) => !!report).map((report) => report.reportID) ?? [];
SearchActions.exportSearchItemsToCSV(
{query: status, jsonQuery: JSON.stringify(queryJSON), reportIDList, transactionIDList: selectedTransactionsKeys, policyIDs: [activeWorkspaceID ?? '']},

Check failure on line 201 in src/components/Search/SearchPageHeader.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

() => {
setIsDownloadErrorModalVisible(true);
},
Expand Down Expand Up @@ -289,6 +298,11 @@
lastPaymentMethods,
]);

const {renderProductTrainingTooltip, shouldShowProductTrainingTooltip, hideProductTrainingTooltip} = useProductTrainingContext(
CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.SEARCH_FILTER_BUTTON_TOOLTIP,
isScreenFocused && !isModalVisible && !!queryJSON && headerButtonsOptions.length === 0,
);

if (shouldUseNarrowLayout) {
if (selectionMode?.isEnabled) {
return (
Expand Down Expand Up @@ -334,6 +348,7 @@
}

const onFiltersButtonPress = () => {
hideProductTrainingTooltip();
const filterFormValues = SearchQueryUtils.buildFilterFormValuesFromQuery(queryJSON, policyCategories, policyTagsLists, currencyList, personalDetails, cardList, reports, taxRates);
SearchActions.updateAdvancedFilters(filterFormValues);

Expand Down Expand Up @@ -362,11 +377,9 @@
vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.BOTTOM,
horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.RIGHT,
}}
shouldUseOverlay
shiftHorizontal={variables.searchFiltersTooltipShiftHorizontal}
wrapperStyle={styles.productTrainingTooltipWrapper}
renderTooltipContent={renderProductTrainingTooltip}
onHideTooltip={hideProductTrainingTooltip}
>
<Button
innerStyles={!isCannedQuery && [styles.searchRouterInputResults, styles.borderNone]}
Expand Down
5 changes: 1 addition & 4 deletions src/components/Tooltip/BaseGenericTooltip/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,7 @@ type BaseGenericTooltipProps = {

/** Handles what to do when hiding the tooltip */
onHideTooltip?: () => void;
} & Pick<
SharedTooltipProps,
'renderTooltipContent' | 'maxWidth' | 'numberOfLines' | 'text' | 'shouldForceRenderingBelow' | 'wrapperStyle' | 'anchorAlignment' | 'shouldUseOverlay' | 'onHideTooltip'
>;
} & Pick<SharedTooltipProps, 'renderTooltipContent' | 'maxWidth' | 'numberOfLines' | 'text' | 'shouldForceRenderingBelow' | 'wrapperStyle' | 'anchorAlignment' | 'shouldUseOverlay'>;

// eslint-disable-next-line import/prefer-default-export
export type {BaseGenericTooltipProps};
Original file line number Diff line number Diff line change
Expand Up @@ -3,39 +3,22 @@ import type {LayoutRectangle, NativeSyntheticEvent} from 'react-native';
import GenericTooltip from '@components/Tooltip/GenericTooltip';
import type {EducationalTooltipProps} from '@components/Tooltip/types';
import measureTooltipCoordinate from './measureTooltipCoordinate';
import * as TooltipManager from './TooltipManager';

type LayoutChangeEventWithTarget = NativeSyntheticEvent<{layout: LayoutRectangle; target: HTMLElement}>;

/**
* A component used to wrap an element intended for displaying a tooltip.
* This tooltip would show immediately without user's interaction and hide after 5 seconds.
*/
function BaseEducationalTooltip({children, onHideTooltip: onHideTooltipProp, shouldRender = false, shouldAutoDismiss = false, ...props}: EducationalTooltipProps) {
function BaseEducationalTooltip({children, shouldRender = false, ...props}: EducationalTooltipProps) {
const hideTooltipRef = useRef<() => void>();

const [shouldMeasure, setShouldMeasure] = useState(false);
const show = useRef<() => void>();
const removeActiveTooltipRef = useRef(() => {});
const removePendingTooltipRef = useRef(() => {});

const didShow = useRef(false);

const onHideTooltip = useCallback(() => {
if (!shouldRender) {
return;
}
onHideTooltipProp?.();
}, [onHideTooltipProp, shouldRender]);

const closeTooltip = useCallback(() => {
if (!didShow.current) {
return;
}
hideTooltipRef.current?.();
onHideTooltip?.();
removeActiveTooltipRef.current();
}, [onHideTooltip]);
}, []);

useEffect(
() => () => {
Expand All @@ -48,36 +31,21 @@ function BaseEducationalTooltip({children, onHideTooltip: onHideTooltipProp, sho
[],
);

// Automatically hide tooltip after 5 seconds
useEffect(() => {
if (!shouldAutoDismiss) {
if (!shouldMeasure) {
return;
}

// Automatically hide tooltip after 5 seconds if shouldAutoDismiss is true
const timerID = setTimeout(() => {
if (!shouldRender) {
closeTooltip();
}, 5000);
return () => {
clearTimeout(timerID);
};
}, [shouldAutoDismiss, closeTooltip]);

useEffect(() => {
if (!shouldMeasure || !shouldRender || didShow.current) {
return;
}
// When tooltip is used inside an animated view (e.g. popover), we need to wait for the animation to finish before measuring content.
const timerID = setTimeout(() => {
removePendingTooltipRef.current();
show.current?.();
didShow.current = true;
removeActiveTooltipRef.current = TooltipManager.addActiveTooltip(closeTooltip);
}, 500);
removePendingTooltipRef.current = TooltipManager.addPendingTooltip(timerID);
return () => {
removePendingTooltipRef.current();
clearTimeout(timerID);
closeTooltip();
};
}, [shouldMeasure, shouldRender, closeTooltip]);

Expand All @@ -87,12 +55,15 @@ function BaseEducationalTooltip({children, onHideTooltip: onHideTooltipProp, sho
[],
);

if (!shouldRender) {
return children as React.ReactElement;
}

return (
<GenericTooltip
shouldForceAnimate
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
onHideTooltip={onHideTooltip}
>
{({showTooltip, hideTooltip, updateTargetBounds}) => {
// eslint-disable-next-line react-compiler/react-compiler
Expand Down
30 changes: 0 additions & 30 deletions src/components/Tooltip/EducationalTooltip/TooltipManager.ts

This file was deleted.

4 changes: 1 addition & 3 deletions src/components/Tooltip/GenericTooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ function GenericTooltip({
},
shouldForceAnimate = false,
shouldUseOverlay: shouldUseOverlayProp = false,
onHideTooltip = () => {},
}: GenericTooltipProps) {
const {preferredLocale} = useLocalize();
const {windowWidth} = useWindowDimensions();
Expand Down Expand Up @@ -150,8 +149,7 @@ function GenericTooltip({
}
setShouldUseOverlay(false);
hideTooltip();
onHideTooltip();
}, [shouldUseOverlay, onHideTooltip, hideTooltip]);
}, [shouldUseOverlay, hideTooltip]);

// Skip the tooltip and return the children if the text is empty, we don't have a render function.
if (StringUtils.isEmptyString(text) && renderTooltipContent == null) {
Expand Down
Loading
Loading