diff --git a/src/hooks/useSearchHighlightAndScroll.ts b/src/hooks/useSearchHighlightAndScroll.ts index 2018373c62c9..c34659a39313 100644 --- a/src/hooks/useSearchHighlightAndScroll.ts +++ b/src/hooks/useSearchHighlightAndScroll.ts @@ -1,3 +1,4 @@ +import {useNavigation} from '@react-navigation/native'; import isEqual from 'lodash/isEqual'; import {useCallback, useEffect, useRef, useState} from 'react'; import type {OnyxCollection, OnyxEntry} from 'react-native-onyx'; @@ -33,9 +34,12 @@ function useSearchHighlightAndScroll({searchResults, transactions, previousTrans const highlightedIDs = useRef>(new Set()); const initializedRef = useRef(false); const isChat = queryJSON.type === CONST.SEARCH.DATA_TYPES.CHAT; + const navigation = useNavigation(); // Trigger search when a new report action is added while on chat or when a new transaction is added for the other search types. useEffect(() => { + const isNavigating = !navigation.isFocused(); + const previousTransactionsIDs = Object.keys(previousTransactions ?? {}); const transactionsIDs = Object.keys(transactions ?? {}); @@ -46,9 +50,10 @@ function useSearchHighlightAndScroll({searchResults, transactions, previousTrans .map((actions) => Object.keys(actions ?? {})) .flat(); - if (searchTriggeredRef.current) { + if (searchTriggeredRef.current || isNavigating) { return; } + const hasTransactionsIDsChange = !isEqual(transactionsIDs, previousTransactionsIDs); const hasReportActionsIDsChange = !isEqual(reportActionsIDs, previousReportActionsIDs); @@ -78,7 +83,7 @@ function useSearchHighlightAndScroll({searchResults, transactions, previousTrans return () => { searchTriggeredRef.current = false; }; - }, [transactions, previousTransactions, queryJSON, offset, reportActions, previousReportActions, isChat]); + }, [transactions, previousTransactions, queryJSON, offset, reportActions, previousReportActions, isChat, navigation]); // Initialize the set with existing IDs only once useEffect(() => { diff --git a/tests/unit/useSearchHighlightAndScrollTest.ts b/tests/unit/useSearchHighlightAndScrollTest.ts index ba17637d5d3f..369213d9ee91 100644 --- a/tests/unit/useSearchHighlightAndScrollTest.ts +++ b/tests/unit/useSearchHighlightAndScrollTest.ts @@ -1,4 +1,6 @@ /* eslint-disable @typescript-eslint/naming-convention */ +import {useNavigation} from '@react-navigation/native'; +import type * as NativeNavigation from '@react-navigation/native'; import {renderHook} from '@testing-library/react-native'; import useSearchHighlightAndScroll from '@hooks/useSearchHighlightAndScroll'; import type {UseSearchHighlightAndScroll} from '@hooks/useSearchHighlightAndScroll'; @@ -6,12 +8,23 @@ import {search} from '@libs/actions/Search'; jest.mock('@libs/actions/Search'); jest.mock('@src/components/ConfirmedRoute.tsx'); - -afterEach(() => { - jest.clearAllMocks(); -}); +jest.mock('@react-navigation/native', () => ({ + ...jest.requireActual('@react-navigation/native'), + useNavigation: jest.fn(), +})); describe('useSearchHighlightAndScroll', () => { + beforeEach(() => { + // Default mock implementation + (useNavigation as jest.Mock).mockReturnValue({ + isFocused: () => true, + }); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + it('should trigger Search when transactionIDs list change', () => { const initialProps: UseSearchHighlightAndScroll = { searchResults: { @@ -194,7 +207,12 @@ describe('useSearchHighlightAndScroll', () => { expect(search).toHaveBeenCalled(); }); - it('should trigger Search when report actions change', () => { + it('should not trigger Search when navigation is not focused', () => { + // Mock navigation.isFocused to return false + (useNavigation as jest.Mock).mockReturnValue({ + isFocused: () => false, + }); + const initialProps: UseSearchHighlightAndScroll = { searchResults: { data: {personalDetailsList: {}}, @@ -212,8 +230,84 @@ describe('useSearchHighlightAndScroll', () => { isLoading: false, }, }, - transactions: {}, - previousTransactions: {}, + transactions: { + transactions_1: { + amount: -100, + bank: '', + billable: false, + cardID: 0, + cardName: 'Cash Expense', + cardNumber: '', + category: '', + comment: { + comment: '', + }, + created: '2025-01-08', + currency: 'ETB', + filename: 'w_c989c343d834d48a4e004c38d03c90bff9434768.png', + inserted: '2025-01-08 15:35:32', + managedCard: false, + merchant: 'g', + modifiedAmount: 0, + modifiedCreated: '', + modifiedCurrency: '', + modifiedMerchant: '', + originalAmount: 0, + originalCurrency: '', + parentTransactionID: '', + posted: '', + receipt: { + receiptID: 7409094723954473, + state: 'SCANCOMPLETE', + source: 'https://www.expensify.com/receipts/w_c989c343d834d48a4e004c38d03c90bff9434768.png', + }, + reimbursable: true, + reportID: '2309609540437471', + status: 'Posted', + tag: '', + transactionID: '1', + hasEReceipt: false, + }, + }, + previousTransactions: { + transactions_1: { + amount: -100, + bank: '', + billable: false, + cardID: 0, + cardName: 'Cash Expense', + cardNumber: '', + category: '', + comment: { + comment: '', + }, + created: '2025-01-08', + currency: 'ETB', + filename: 'w_c989c343d834d48a4e004c38d03c90bff9434768.png', + inserted: '2025-01-08 15:35:32', + managedCard: false, + merchant: 'g', + modifiedAmount: 0, + modifiedCreated: '', + modifiedCurrency: '', + modifiedMerchant: '', + originalAmount: 0, + originalCurrency: '', + parentTransactionID: '', + posted: '', + receipt: { + receiptID: 7409094723954473, + state: 'SCANCOMPLETE', + source: 'https://www.expensify.com/receipts/w_c989c343d834d48a4e004c38d03c90bff9434768.png', + }, + reimbursable: true, + reportID: '2309609540437471', + status: 'Posted', + tag: '', + transactionID: '1', + hasEReceipt: false, + }, + }, reportActions: { reportActions_209647397999267: { 1: { @@ -245,34 +339,61 @@ describe('useSearchHighlightAndScroll', () => { }, offset: 0, }; - - const changedProps: UseSearchHighlightAndScroll = { + const changedProp: UseSearchHighlightAndScroll = { ...initialProps, - reportActions: { - reportActions_209647397999268: { - 1: { - actionName: 'POLICYCHANGELOG_CORPORATE_UPGRADE', - reportActionID: '1', - created: '', + transactions: { + transactions_2: { + amount: -100, + bank: '', + billable: false, + cardID: 0, + cardName: 'Cash Expense', + cardNumber: '', + category: '', + comment: { + comment: '', }, - 2: { - actionName: 'ADDCOMMENT', - reportActionID: '2', - created: '', + created: '2025-01-08', + currency: 'ETB', + filename: 'w_c989c343d834d48a4e004c38d03c90bff9434768.png', + inserted: '2025-01-08 15:35:32', + managedCard: false, + merchant: 'g', + modifiedAmount: 0, + modifiedCreated: '', + modifiedCurrency: '', + modifiedMerchant: '', + originalAmount: 0, + originalCurrency: '', + parentTransactionID: '', + posted: '', + receipt: { + receiptID: 7409094723954473, + state: 'SCANCOMPLETE', + source: 'https://www.expensify.com/receipts/w_c989c343d834d48a4e004c38d03c90bff9434768.png', }, + reimbursable: true, + reportID: '2309609540437471', + status: 'Posted', + tag: '', + transactionID: '2', + hasEReceipt: false, }, }, }; - const {rerender} = renderHook((props: UseSearchHighlightAndScroll) => useSearchHighlightAndScroll(props), { + // Reset the mock to track new calls + jest.clearAllMocks(); + + const {rerender} = renderHook((prop: UseSearchHighlightAndScroll) => useSearchHighlightAndScroll(prop), { initialProps, }); expect(search).not.toHaveBeenCalled(); - // When report actions change - rerender(changedProps); + // When the transaction ids list changes but navigation is not focused + rerender(changedProp); - // Then Search will be triggered - expect(search).toHaveBeenCalled(); + // Then Search should NOT be triggered + expect(search).not.toHaveBeenCalled(); }); });