Skip to content
Open
Show file tree
Hide file tree
Changes from all 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/hooks/useSearchSections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ function useSearchSections(): UseSearchSectionsResult {
formatPhoneNumber,
bankAccountList,
groupBy,
queryJSON: lastSearchQuery?.queryJSON,
currentSearch: searchKey,
reportNameValuePairs,
isActionLoadingSet,
Expand Down
2 changes: 1 addition & 1 deletion src/libs/SearchUIUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2996,7 +2996,7 @@ function getReportSections({
reportActions[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportItem.reportID}`] ?? Object.values(data[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportItem.reportID}`] ?? {});

const isActionLoading = !!isActionLoadingSet?.has(`${ONYXKEYS.COLLECTION.RAM_ONLY_REPORT_LOADING_STATE}${reportItem.reportID}`);
const shouldShow = !isActionLoading && currentQueryJSON?.type === CONST.SEARCH.DATA_TYPES.EXPENSE ? isEligibleForStatus(currentQueryJSON, reportItem) : true;
const shouldShow = isActionLoading || isEligibleForStatus(currentQueryJSON, reportItem);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve pagination count when locally hiding reports

When a status-filtered Reports search has exactly one loaded page and hasMoreResults is still true, this new local status filter can drop the updated report before the next page is fetched, so getReportSections() returns the post-filter length as allDataLength. Search/index.tsx then blocks pagination with offset > allDataLength - CONST.SEARCH.RESULTS_PAGE_SIZE (for example, 0 > 49 - 50), so users with more than 50 matching reports who submit/approve/pay one before scrolling can no longer load the remaining matches until they rerun the search. Please keep the pagination count based on the unfiltered snapshot length, or otherwise avoid feeding this post-filter length into the fetch-more guard.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@mukhrr Please check if it's a valid case.


if (shouldShow) {
const reportPendingAction =
Expand Down
55 changes: 55 additions & 0 deletions tests/unit/Search/SearchUIUtilsTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5982,6 +5982,21 @@ describe('SearchUIUtils', () => {
};
}

// `getSections` only routes to `getReportSections` for `expense-report`, so an `expense` query never
// exercises that path in production.
function makeExpenseReportQueryJSON(status: string[] | undefined, isNegated = false) {
return {
...makeExpenseQueryJSON(status, isNegated),
type: CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT,
inputQuery: 'type:expense-report' as const,
filters: {
operator: CONST.SEARCH.SYNTAX_OPERATORS.AND,
left: CONST.SEARCH.SYNTAX_FILTER_KEYS.TYPE,
right: CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT,
},
};
}

describe('getTransactionsSections filtering and edge cases', () => {
const filterTestReportID = 'filter-report-1';
const filterTestTxID = 'filter-tx-1';
Expand Down Expand Up @@ -6427,6 +6442,46 @@ describe('SearchUIUtils', () => {
expect(sections.some((s) => s.keyForList === rptFilterReportID)).toBe(true);
});

it('should exclude a submitted report from the drafts filter on an expense-report query', () => {
const data = makeReportFilterTestData({stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, statusNum: CONST.REPORT.STATUS_NUM.SUBMITTED, type: CONST.REPORT.TYPE.EXPENSE});
const [sections] = callGetReportSections(data, {queryJSON: makeExpenseReportQueryJSON([CONST.SEARCH.STATUS.EXPENSE.DRAFTS])});
expect(sections.some((s) => s.keyForList === rptFilterReportID)).toBe(false);
});

it('should keep a draft report under the drafts filter on an expense-report query', () => {
const data = makeReportFilterTestData({stateNum: CONST.REPORT.STATE_NUM.OPEN, statusNum: CONST.REPORT.STATUS_NUM.OPEN, type: CONST.REPORT.TYPE.EXPENSE});
const [sections] = callGetReportSections(data, {queryJSON: makeExpenseReportQueryJSON([CONST.SEARCH.STATUS.EXPENSE.DRAFTS])});
expect(sections.some((s) => s.keyForList === rptFilterReportID)).toBe(true);
});

it('should exclude a paid report from the approved filter on an expense-report query', () => {
const data = makeReportFilterTestData({stateNum: CONST.REPORT.STATE_NUM.APPROVED, statusNum: CONST.REPORT.STATUS_NUM.REIMBURSED, type: CONST.REPORT.TYPE.EXPENSE});
const [sections] = callGetReportSections(data, {queryJSON: makeExpenseReportQueryJSON([CONST.SEARCH.STATUS.EXPENSE.APPROVED])});
expect(sections.some((s) => s.keyForList === rptFilterReportID)).toBe(false);
});

it('should show every report when an expense-report query has no status filter', () => {
const data = makeReportFilterTestData({stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, statusNum: CONST.REPORT.STATUS_NUM.SUBMITTED, type: CONST.REPORT.TYPE.EXPENSE});
const [sections] = callGetReportSections(data, {queryJSON: makeExpenseReportQueryJSON(undefined)});
expect(sections.some((s) => s.keyForList === rptFilterReportID)).toBe(true);
});

it('should honor a negated status filter on an expense-report query', () => {
const data = makeReportFilterTestData({stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, statusNum: CONST.REPORT.STATUS_NUM.SUBMITTED, type: CONST.REPORT.TYPE.EXPENSE});
const [sections] = callGetReportSections(data, {queryJSON: makeExpenseReportQueryJSON([CONST.SEARCH.STATUS.EXPENSE.OUTSTANDING], true)});
expect(sections.some((s) => s.keyForList === rptFilterReportID)).toBe(false);
});

it('should keep a report visible while its action is loading on an expense-report query', () => {
const data = makeReportFilterTestData({stateNum: CONST.REPORT.STATE_NUM.SUBMITTED, statusNum: CONST.REPORT.STATUS_NUM.SUBMITTED, type: CONST.REPORT.TYPE.EXPENSE});
const loadingSet = new Set([`${ONYXKEYS.COLLECTION.RAM_ONLY_REPORT_LOADING_STATE}${rptFilterReportID}`]);
const [sections] = callGetReportSections(data, {
queryJSON: makeExpenseReportQueryJSON([CONST.SEARCH.STATUS.EXPENSE.DRAFTS]),
isActionLoadingSet: loadingSet,
});
expect(sections.some((s) => s.keyForList === rptFilterReportID)).toBe(true);
});

it('should drop transactions when their parent report is filtered out', () => {
const data = makeReportFilterTestData({stateNum: CONST.REPORT.STATE_NUM.OPEN, statusNum: CONST.REPORT.STATUS_NUM.OPEN, type: CONST.REPORT.TYPE.EXPENSE});
const [sections] = callGetReportSections(data, {queryJSON: makeExpenseQueryJSON([CONST.SEARCH.STATUS.EXPENSE.OUTSTANDING])});
Expand Down
Loading