Skip to content

Update inbox tooltip and add expense report filter type tooltip #58721

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

Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
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: 1 addition & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6927,6 +6927,7 @@ const CONST = {
SCAN_TEST_TOOLTIP: 'scanTestTooltip',
SCAN_TEST_TOOLTIP_MANAGER: 'scanTestTooltipManager',
SCAN_TEST_CONFIRMATION: 'scanTestConfirmation',
EXPENSE_REPORTS_FILTER: 'expenseReportsFilter',
},
CHANGE_POLICY_TRAINING_MODAL: 'changePolicyModal',
SMART_BANNER_HEIGHT: 152,
Expand Down
26 changes: 21 additions & 5 deletions src/components/ProductTrainingContext/TOOLTIPS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,15 @@ const {
SCAN_TEST_TOOLTIP,
SCAN_TEST_TOOLTIP_MANAGER,
SCAN_TEST_CONFIRMATION,
EXPENSE_REPORTS_FILTER,
} = CONST.PRODUCT_TRAINING_TOOLTIP_NAMES;

type ProductTrainingTooltipName = ValueOf<typeof CONST.PRODUCT_TRAINING_TOOLTIP_NAMES>;

type ShouldShowConditionProps = {
shouldUseNarrowLayout?: boolean;
shouldUseNarrowLayout: boolean;
isUserPolicyAdmin: boolean;
hasBeenAddedToNudgeMigration: boolean;
};

type TooltipData = {
Expand Down Expand Up @@ -65,14 +68,15 @@ const TOOLTIPS: Record<ProductTrainingTooltipName, TooltipData> = {
},
[BOTTOM_NAV_INBOX_TOOLTIP]: {
content: [
{text: 'productTrainingTooltip.bottomNavInboxTooltip.part1', isBold: true},
{text: 'productTrainingTooltip.bottomNavInboxTooltip.part2', isBold: false},
{text: 'productTrainingTooltip.bottomNavInboxTooltip.part1', isBold: false},
{text: 'productTrainingTooltip.bottomNavInboxTooltip.part2', isBold: true},
{text: 'productTrainingTooltip.bottomNavInboxTooltip.part3', isBold: false},
{text: 'productTrainingTooltip.bottomNavInboxTooltip.part4', isBold: true},
],
onHideTooltip: (isDismissedUsingCloseButton = false) => dismissProductTraining(BOTTOM_NAV_INBOX_TOOLTIP, isDismissedUsingCloseButton),
name: BOTTOM_NAV_INBOX_TOOLTIP,
priority: 900,
shouldShow: () => true,
priority: 1700,
shouldShow: ({hasBeenAddedToNudgeMigration}) => hasBeenAddedToNudgeMigration,
},
[LHN_WORKSPACE_CHAT_TOOLTIP]: {
content: [
Expand All @@ -85,6 +89,18 @@ const TOOLTIPS: Record<ProductTrainingTooltipName, TooltipData> = {
priority: 800,
shouldShow: () => true,
},
[EXPENSE_REPORTS_FILTER]: {
content: [
{text: 'productTrainingTooltip.expenseReportsFilter.part1', isBold: false},
{text: 'productTrainingTooltip.expenseReportsFilter.part2', isBold: true},
{text: 'productTrainingTooltip.expenseReportsFilter.part3', isBold: false},
],
onHideTooltip: () => dismissProductTraining(EXPENSE_REPORTS_FILTER),
name: EXPENSE_REPORTS_FILTER,
priority: 2000,
shouldShow: ({shouldUseNarrowLayout, isUserPolicyAdmin, hasBeenAddedToNudgeMigration}: ShouldShowConditionProps) =>
!shouldUseNarrowLayout && isUserPolicyAdmin && hasBeenAddedToNudgeMigration,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets confirm if we would like to show this on mobile or not

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I followed the RENAME_SAVED_SEARCH tooltip, which is displayed in the same location and hidden in smaller layouts:

[RENAME_SAVED_SEARCH]: {
content: [
{text: 'productTrainingTooltip.saveSearchTooltip.part1', isBold: true},
{text: 'productTrainingTooltip.saveSearchTooltip.part2', isBold: false},
],
onHideTooltip: (isDismissedUsingCloseButton = false) => dismissProductTraining(RENAME_SAVED_SEARCH, isDismissedUsingCloseButton),
name: RENAME_SAVED_SEARCH,
priority: 1250,
shouldShow: ({shouldUseNarrowLayout}) => !shouldUseNarrowLayout,

},
[SCAN_TEST_TOOLTIP]: {
content: [
{text: 'productTrainingTooltip.scanTestTooltip.part1', isBold: false},
Expand Down
33 changes: 28 additions & 5 deletions src/components/ProductTrainingContext/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import useThemeStyles from '@hooks/useThemeStyles';
import {parseFSAttributes} from '@libs/Fullstory';
import getPlatform from '@libs/getPlatform';
import {hasCompletedGuidedSetupFlowSelector} from '@libs/onboardingSelectors';
import {getActiveAdminWorkspaces} from '@libs/PolicyUtils';
import isProductTrainingElementDismissed from '@libs/TooltipUtils';
import variables from '@styles/variables';
import CONST from '@src/CONST';
Expand Down Expand Up @@ -48,17 +49,28 @@ const ProductTrainingContext = createContext<ProductTrainingContextType>({
});

function ProductTrainingContextProvider({children}: ChildrenProps) {
const [isLoadingApp] = useOnyx(ONYXKEYS.IS_LOADING_APP, {initialValue: true});
const [tryNewDot] = useOnyx(ONYXKEYS.NVP_TRYNEWDOT);
const [isLoadingApp] = useOnyx(ONYXKEYS.IS_LOADING_APP, {initialValue: true, canBeMissing: true});
const [tryNewDot] = useOnyx(ONYXKEYS.NVP_TRYNEWDOT, {canBeMissing: true});
const hasBeenAddedToNudgeMigration = !!tryNewDot?.nudgeMigration?.timestamp;
const [isOnboardingCompleted = true, isOnboardingCompletedMetadata] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {
selector: hasCompletedGuidedSetupFlowSelector,
canBeMissing: true,
});

const [dismissedProductTraining] = useOnyx(ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING);
const [allPolicies, allPoliciesMetadata] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {canBeMissing: true});
const [currentUserLogin, currentUserLoginMetadata] = useOnyx(ONYXKEYS.SESSION, {selector: (session) => session?.email, canBeMissing: true});

const isUserPolicyAdmin = useMemo(() => {
if (!allPolicies || !currentUserLogin || isLoadingOnyxValue(allPoliciesMetadata, currentUserLoginMetadata)) {
return false;
}
return getActiveAdminWorkspaces(allPolicies, currentUserLogin).length > 0;
}, [allPolicies, currentUserLogin, allPoliciesMetadata, currentUserLoginMetadata]);

const [dismissedProductTraining] = useOnyx(ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING, {canBeMissing: true});
const {shouldUseNarrowLayout} = useResponsiveLayout();

const [modal] = useOnyx(ONYXKEYS.MODAL);
const [modal] = useOnyx(ONYXKEYS.MODAL, {canBeMissing: true});
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
const isModalVisible = modal?.isVisible || modal?.willAlertModalBecomeVisible;

Expand Down Expand Up @@ -129,9 +141,20 @@ function ProductTrainingContextProvider({children}: ChildrenProps) {

return tooltipConfig.shouldShow({
shouldUseNarrowLayout,
isUserPolicyAdmin,
hasBeenAddedToNudgeMigration,
});
},
[dismissedProductTraining, hasBeenAddedToNudgeMigration, isOnboardingCompleted, isOnboardingCompletedMetadata, shouldUseNarrowLayout, isModalVisible, isLoadingApp],
[
dismissedProductTraining,
hasBeenAddedToNudgeMigration,
isOnboardingCompleted,
isOnboardingCompletedMetadata,
shouldUseNarrowLayout,
isModalVisible,
isLoadingApp,
isUserPolicyAdmin,
],
);

const registerTooltip = useCallback(
Expand Down
12 changes: 9 additions & 3 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6208,9 +6208,10 @@ const translations = {
part2: ' here!',
},
bottomNavInboxTooltip: {
part1: 'Your to-do list',
part2: '\n🟢 = ready for you',
part3: ' 🔴 = needs review',
part1: 'Check what ',
part2: 'needs your attention',
part3: '\nand ',
part4: 'chat about expenses.',
},
workspaceChatTooltip: {
part1: 'Submit expenses',
Expand All @@ -6222,6 +6223,11 @@ const translations = {
part2: ', start chatting,',
part3: '\nand more!',
},
expenseReportsFilter: {
part1: 'Welcome! Find all of your',
part2: "\ncompany's reports",
part3: ' here.',
},
scanTestTooltip: {
part1: 'Want to see how Scan works?',
part2: ' Try a test receipt!',
Expand Down
12 changes: 9 additions & 3 deletions src/languages/es.ts
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i assume Translation confirmation needed

Original file line number Diff line number Diff line change
Expand Up @@ -6731,9 +6731,10 @@ const translations = {
part2: ' aquí',
},
bottomNavInboxTooltip: {
part1: 'Tu lista de tareas',
part2: '\n🟢 = listo para ti',
part3: ' 🔴 = necesita revisión',
part1: 'Revisa lo que ',
part2: 'necesita tu atención',
part3: '\ny ',
part4: 'chatea sobre los gastos.',
},
workspaceChatTooltip: {
part1: 'Envía gastos',
Expand All @@ -6745,6 +6746,11 @@ const translations = {
part2: ', comienza a chatear,',
part3: '\ny mucho más!',
},
expenseReportsFilter: {
part1: '¡Bienvenido! Aquí encontrarás todos los',
part2: '\ninformes de tu empresa',
part3: '.',
},
scanTestTooltip: {
part1: '¿Quieres ver cómo funciona Escanear?',
part2: '¡Prueba con un recibo de prueba!',
Expand Down
54 changes: 37 additions & 17 deletions src/pages/Search/SearchTypeMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,21 +52,27 @@ function SearchTypeMenu({queryJSON}: SearchTypeMenuProps) {
const {singleExecution} = useSingleExecution();
const {translate} = useLocalize();
const {canUseLeftHandBar} = usePermissions();
const [savedSearches] = useOnyx(ONYXKEYS.SAVED_SEARCHES);
const [savedSearches] = useOnyx(ONYXKEYS.SAVED_SEARCHES, {canBeMissing: true});
const {isOffline} = useNetwork();
const shouldShowSavedSearchesMenuItemTitle = Object.values(savedSearches ?? {}).filter((s) => s.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || isOffline).length > 0;
const isFocused = useIsFocused();
const {shouldShowProductTrainingTooltip, renderProductTrainingTooltip, hideProductTrainingTooltip} = useProductTrainingContext(
CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.RENAME_SAVED_SEARCH,
shouldShowSavedSearchesMenuItemTitle && isFocused,
);
const {
shouldShowProductTrainingTooltip: shouldShowSavedSearchTooltip,
renderProductTrainingTooltip: renderSavedSearchTooltip,
hideProductTrainingTooltip: hideSavedSearchTooltip,
} = useProductTrainingContext(CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.RENAME_SAVED_SEARCH, shouldShowSavedSearchesMenuItemTitle && isFocused);
const {
shouldShowProductTrainingTooltip: shouldShowExpenseReportsTypeTooltip,
renderProductTrainingTooltip: renderExpenseReportsTypeTooltip,
hideProductTrainingTooltip: hideExpenseReportsTypeTooltip,
} = useProductTrainingContext(CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.EXPENSE_REPORTS_FILTER, true);
const {showDeleteModal, DeleteConfirmModal} = useDeleteSavedSearch();
const [session] = useOnyx(ONYXKEYS.SESSION);
const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY);
const [session] = useOnyx(ONYXKEYS.SESSION, {canBeMissing: true});
const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {canBeMissing: true});
const personalDetails = usePersonalDetails();
const [reports] = useOnyx(ONYXKEYS.COLLECTION.REPORT);
const [userCardList] = useOnyx(ONYXKEYS.CARD_LIST);
const [workspaceCardFeeds] = useOnyx(ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST);
const [reports] = useOnyx(ONYXKEYS.COLLECTION.REPORT, {canBeMissing: true});
const [userCardList] = useOnyx(ONYXKEYS.CARD_LIST, {canBeMissing: true});
const [workspaceCardFeeds] = useOnyx(ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST, {canBeMissing: true});
const allCards = useMemo(() => mergeCardListWithWorkspaceFeeds(workspaceCardFeeds ?? CONST.EMPTY_OBJECT, userCardList), [userCardList, workspaceCardFeeds]);
const taxRates = getAllTaxRates();
const {clearSelectedTransactions} = useSearchContext();
Expand Down Expand Up @@ -102,9 +108,9 @@ function SearchTypeMenu({queryJSON}: SearchTypeMenuProps) {
<SavedSearchItemThreeDotMenu
menuItems={getOverflowMenu(title, Number(key), item.query)}
isDisabledItem={item.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE}
hideProductTrainingTooltip={index === 0 && shouldShowProductTrainingTooltip ? hideProductTrainingTooltip : undefined}
shouldRenderTooltip={index === 0 && shouldShowProductTrainingTooltip}
renderTooltipContent={renderProductTrainingTooltip}
hideProductTrainingTooltip={index === 0 && shouldShowSavedSearchTooltip ? hideSavedSearchTooltip : undefined}
shouldRenderTooltip={index === 0 && shouldShowSavedSearchTooltip}
renderTooltipContent={renderSavedSearchTooltip}
/>
),
style: [styles.alignItemsCenter],
Expand All @@ -115,23 +121,23 @@ function SearchTypeMenu({queryJSON}: SearchTypeMenuProps) {
tooltipShiftHorizontal: variables.savedSearchShiftHorizontal,
tooltipShiftVertical: variables.savedSearchShiftVertical,
tooltipWrapperStyle: [styles.mh4, styles.pv2, styles.productTrainingTooltipWrapper],
renderTooltipContent: renderProductTrainingTooltip,
renderTooltipContent: renderSavedSearchTooltip,
};
},
[
allCards,
hash,
getOverflowMenu,
shouldShowSavedSearchTooltip,
hideSavedSearchTooltip,
styles.alignItemsCenter,
styles.mh4,
styles.pv2,
styles.productTrainingTooltipWrapper,
renderSavedSearchTooltip,
personalDetails,
reports,
taxRates,
shouldShowProductTrainingTooltip,
hideProductTrainingTooltip,
renderProductTrainingTooltip,
cardFeedNamesWithType,
allPolicies,
canUseLeftHandBar,
Expand Down Expand Up @@ -211,7 +217,12 @@ function SearchTypeMenu({queryJSON}: SearchTypeMenuProps) {
>
<View style={[styles.pb4, styles.mh3, styles.mt3]}>
{typeMenuItems.map((item, index) => {
const shouldShowTooltip = item.translationPath === 'common.expenseReports' && index !== activeItemIndex && shouldShowExpenseReportsTypeTooltip;

const onPress = singleExecution(() => {
if (shouldShowTooltip) {
hideExpenseReportsTypeTooltip();
}
clearAllFilters();
clearSelectedTransactions();
Navigation.navigate(item.getRoute(queryJSON?.policyID));
Expand All @@ -230,6 +241,15 @@ function SearchTypeMenu({queryJSON}: SearchTypeMenuProps) {
focused={index === activeItemIndex}
onPress={onPress}
shouldIconUseAutoWidthStyle
shouldRenderTooltip={shouldShowTooltip}
renderTooltipContent={renderExpenseReportsTypeTooltip}
tooltipAnchorAlignment={{
horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.LEFT,
vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.TOP,
}}
tooltipShiftHorizontal={variables.expenseReportsTypeTooltipShiftHorizontal}
tooltipWrapperStyle={styles.productTrainingTooltipWrapper}
onEducationTooltipPress={onPress}
/>
);
})}
Expand Down
1 change: 1 addition & 0 deletions src/styles/variables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,7 @@ export default {
savedSearchShiftHorizontal: -10,
savedSearchShiftVertical: 6,
navigationTabBarInboxTooltipShiftHorizontal: 36,
expenseReportsTypeTooltipShiftHorizontal: 10,

inlineImagePreviewMinSize: 64,
inlineImagePreviewMaxSize: 148,
Expand Down
6 changes: 6 additions & 0 deletions src/types/onyx/DismissedProductTraining.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const {
SCAN_TEST_TOOLTIP,
SCAN_TEST_TOOLTIP_MANAGER,
SCAN_TEST_CONFIRMATION,
EXPENSE_REPORTS_FILTER,
} = CONST.PRODUCT_TRAINING_TOOLTIP_NAMES;

/**
Expand Down Expand Up @@ -72,6 +73,11 @@ type DismissedProductTraining = {
*/
[SCAN_TEST_CONFIRMATION]: DismissedProductTrainingElement;

/**
* When user dismisses the expenseReportsFilter product training tooltip, we store the timestamp here.
*/
[EXPENSE_REPORTS_FILTER]: DismissedProductTrainingElement;

/**
* When user dismisses the ChangeReportPolicy feature training modal, we store the timestamp here.
*/
Expand Down
21 changes: 0 additions & 21 deletions tests/ui/components/ProductTrainingContextProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -260,27 +260,6 @@ describe('ProductTrainingContextProvider', () => {
});

describe('Layout Specific Behavior', () => {
it('should handle narrow layout specific tooltips based on screen width', async () => {
// When narrow layout is false
mockUseResponsiveLayout.mockReturnValue({...DEFAULT_USE_RESPONSIVE_LAYOUT_VALUE, shouldUseNarrowLayout: false});

Onyx.merge(ONYXKEYS.NVP_ONBOARDING, {hasCompletedGuidedSetupFlow: true});
await waitForBatchedUpdatesWithAct();

// TODO: To be replaced by expense reports search tooltip
const testTooltip = CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.CONCEIRGE_LHN_GBR;
const {result, rerender} = renderHook(() => useProductTrainingContext(testTooltip), {wrapper});
// Then narrow layout tooltip should not show
expect(result.current.shouldShowProductTrainingTooltip).toBe(false);

// When narrow layout changes to true
mockUseResponsiveLayout.mockReturnValue({...DEFAULT_USE_RESPONSIVE_LAYOUT_VALUE, shouldUseNarrowLayout: true});
rerender({});
await waitForBatchedUpdatesWithAct();

// Then narrow layout tooltip should show
expect(result.current.shouldShowProductTrainingTooltip).toBe(false);
});
it('should handle wide layout specific tooltips based on screen width', async () => {
// When narrow layout is true
mockUseResponsiveLayout.mockReturnValue({...DEFAULT_USE_RESPONSIVE_LAYOUT_VALUE, shouldUseNarrowLayout: true});
Expand Down