Skip to content

[$250] Spend - Default sort is descending #91935

Open

Description

@applause-bot

If you haven’t already, check out our contributing guidelines for onboarding and email contributors@expensify.com to request to join our Slack channel!


Version Number: 9.3.84-0
Reproducible in staging?: Yes
Reproducible in production?: No
If this was caught during regression testing, add the test name, ID and link from BrowserStack: #91710
Email or phone of affected tester (no customers): applausetester+05272026_1@applause.expensifail.com
Issue reported by: Applause Internal Team
Bug source: Pull Request QA execution
Device used: Windows 11/ Chrome
App Component: Other

Action Performed:

  1. Open the staging.new.expensify.com website.
  2. Create new report
  3. Add two expenses to the report and force the second one of them to have a violation
  4. Go to Spend -> Reports
  5. Open the created report
  6. Change sort to Total

Expected Result:

Verify you have the default sort (Date/ASC) set. RBR pre-sort is gone.

Actual Result:

Default sort for Date is set to DESC. RBR pre-sort is still at the top

Workaround:

Unknown

Platforms:

  • Android: App
  • Android: mWeb Chrome
  • iOS: App
  • iOS: mWeb Safari
  • iOS: mWeb Chrome
  • Windows: Chrome
  • MacOS: Chrome Safari

Screenshots/Videos

bandicam.2026-05-28.09-08-42-638.1.mp4

View all open jobs on GitHub

Applause Internal Information
Exported by: Julia Ponikarchuk
Bug ID: 7164951

Activity

  1. applause-bot commented on May 28, 2026

    @applause-bot
    Author

    While Applause doesn't embed attachments for this bug, here are the links for quick view:
    https://platform.applause.com/services/links/v1/external/297f145c1eeb20e9d3e55802cae6333624c7b62935f10115ecdb0c62624c9de0

  2. added
    BugSomething is broken. Auto assigns a BugZero manager.
    on May 28, 2026
  3. MelvinBot commented on May 28, 2026

    @MelvinBot
    Contributor

    Proposal

    What is the root cause of that problem?

    The sort toggle logic in SortableHeaderText.tsx:60 always defaults to DESC when clicking a column that isn't currently active:

    const nextSortOrder = isActive && sortOrder === DESC ? ASC : DESC;

    When isActive is false (i.e., switching to a different column), the ternary short-circuits to DESC regardless. So the flow is:

    1. Report opens with Date/ASC (correct initial state at MoneyRequestReportTransactionList.tsx:285-288)
    2. User clicks Total → isActive=false → sort becomes Total/DESC
    3. User clicks Date again → isActive=false → sort becomes Date/DESC (bug: should be ASC)

    What changes do you think we should make in order to solve the problem?

    Modify the nextSortOrder logic in SortableHeaderText.tsx:60 so that when switching to a non-active column, the default sort order is ASC instead of DESC. The toggle behavior (clicking an already-active column flips the order) should remain unchanged.

    What alternative solutions did you explore? (Optional)

    An alternative would be to handle this in the onSortPress callback of MoneyRequestReportTransactionList.tsx:741 by overriding the sort order when the column changes. However, fixing it in SortableHeaderText is cleaner since it's the shared component responsible for computing the next sort order and the same issue would affect any consumer. Note that this change would also affect the Search table headers that use the same component — this is likely the correct behavior there too (defaulting to ASC when first clicking a column), but should be verified during QA.


    Next Steps for Contributor+ team: Reply with @MelvinBot implement this to create a draft PR, @MelvinBot <your feedback> to refine this analysis, or explain why you are rejecting Melvin's proposal.

  4. added
    ExternalAdded to denote the issue can be worked on by a contributor
    on May 28, 2026
  5. added
    Help WantedApply this label when an issue is open to proposals by contributors
    on May 28, 2026
  6. yusufdeveloper2903 commented on May 28, 2026

    @yusufdeveloper2903
    Contributor

    🚨 Edited by proposal-police: This proposal was edited at 2026-08-26 09:34:11 UTC.

    Proposal

    What is the root cause of that problem?

    The report transaction table declares its own default sort as Date/ASC, and gates the RBR pre-sort on exactly that pair:

    const [sortConfig, setSortConfig] = useState<SortedTransactions>({
    sortBy: CONST.SEARCH.TABLE_COLUMNS.DATE,
    sortOrder: CONST.SEARCH.SORT_ORDER.ASC,
    });
    const {sortBy, sortOrder} = sortConfig;
    const isDefaultSort = sortBy === CONST.SEARCH.TABLE_COLUMNS.DATE && sortOrder === CONST.SEARCH.SORT_ORDER.ASC;

    The RBR set is null for any other pair, which is what switches violation-first ordering off:

    const rbrTransactionIDs = useMemo(() => {
    if (!isDefaultSort || !allTransactionViolations) {
    return null;
    }

    But the direction a header click produces is not decided here. It is decided by the shared SortableHeaderText, which anchors the first click of any inactive column to DESC:

    const nextSortOrder = isActive && sortOrder === CONST.SEARCH.SORT_ORDER.DESC ? CONST.SEARCH.SORT_ORDER.ASC : CONST.SEARCH.SORT_ORDER.DESC;
    return (
    <View style={containerStyle}>
    <PressableWithFeedback
    onPress={() => onPress(nextSortOrder)}

    SortableTableHeader forwards that order verbatim, and this table's onSortPress writes it into state without translating it to the view's own default:

    onSortPress={(selectedSortBy, selectedSortOrder) => {
    if (!isSortableColumnName(selectedSortBy)) {
    return;
    }
    setSortConfig((prevState) => ({...prevState, sortBy: selectedSortBy, sortOrder: selectedSortOrder}));
    }}

    So after Date → Amount → Date the table lands on Date/DESC, isDefaultSort stays false, and the flagged expense stops floating to the top.

    I measured the emitted order rather than inferring it. Rendering the real MoneyRequestReportTableHeader in Jest with Amount active and pressing Date emits ['date', 'desc'], and driving PR #91710's steps 5 → 7 → 8 through the same onSortPress body fails on current main with:

    -   "isDefaultSort": true,     -   "sortOrder": "asc",
    +   "isDefaultSort": false,    +   "sortOrder": "desc",
    

    When it became wrong

    The DESC anchor is correct for the component's other consumer: Search's own Date sort is DESC.

    const newQueryJSON: SearchQueryJSON = {...queryJSON, groupBy: undefined, sortBy: CONST.SEARCH.TABLE_COLUMNS.DATE, sortOrder: CONST.SEARCH.SORT_ORDER.DESC, flatFilters: newFlatFilters};

    The two defaults diverged on 2025-03-24, when this table's initial order was changed from DESC to ASC in #58903:

    const [sortedData, setSortedData] = useState<SortedTransactions>({
    transactions,
    sortBy: CONST.SEARCH.TABLE_COLUMNS.DATE,
    sortOrder: CONST.SEARCH.SORT_ORDER.ASC,
    });

    That made the shared anchor wrong for this view immediately, but with no visible effect until the isDefaultSort gate gave the lost default a consequence.

    Why the listed steps do not reproduce it

    @rojiphil the OP's step list stops at "Change sort to Total". PR #91710's test plan has an eighth step — "Switch back to Date/ASC and verify RBR-flagged item is back on the top" — and the Applause export dropped it. Following the six listed steps, nothing is wrong.

    The second reason it is easy to miss: the Date sort value is created, which is written date-only.

    const created = currentDate ?? format(new Date(), 'yyyy-MM-dd');

    App/src/libs/ReportUtils.ts

    Lines 14103 to 14105 in 9e84742

    switch (key) {
    case CONST.SEARCH.TABLE_COLUMNS.DATE:
    return getTransactionCreated(transaction);

    Two expenses created on the same day compare equal, and Array.prototype.sort is stable, so ASC and DESC render the same rows in the same order — only the arrow flips. Expenses on different days separate cleanly.

    On the second sentence of the Actual Result ("RBR pre-sort is still at the top"): with Amount active, isDefaultSort is false and rbrTransactionIDs is null — my run asserts that — so nothing pins the flagged row there. If it is still on top, it also wins the amount comparison. That is not a second defect, and a tie-break in the comparator cannot change the sort direction the title is about.

    Nothing catches it: SortableHeaderText and SortableTableHeader have no test anywhere in tests/, and the only unit test for this component replicates one effect in isolation instead of rendering the header.

    /**
    * This hook replicates the active transaction IDs logic from MoneyRequestReportTransactionList
    * to allow isolated testing of the useEffect behavior.
    */
    function useActiveTransactionIDsEffect(visualOrderTransactionIDs: string[]) {

    What changes do you think we should make in order to solve the problem?

    First, a correction to my own earlier proposal. I suggested a defaultSortOrder prop on SortableHeaderText. That is wrong: the prop is per table, not per column, so passing ASC would also flip the first click of Amount, Merchant, Description, Category and Tag in this same table. That is a behaviour change well beyond this bug.

    The default belongs to the view, so the translation belongs in the view. Hoist the pair this file already spells out twice into one constant, and reuse it in the handler:

    +const DEFAULT_SORT_CONFIG: SortedTransactions = {
    +    sortBy: CONST.SEARCH.TABLE_COLUMNS.DATE,
    +    sortOrder: CONST.SEARCH.SORT_ORDER.ASC,
    +};
    -    const [sortConfig, setSortConfig] = useState<SortedTransactions>({sortBy: DATE, sortOrder: ASC});
    -    const isDefaultSort = sortBy === CONST.SEARCH.TABLE_COLUMNS.DATE && sortOrder === CONST.SEARCH.SORT_ORDER.ASC;
    +    const [sortConfig, setSortConfig] = useState<SortedTransactions>(DEFAULT_SORT_CONFIG);
    +    const isDefaultSort = sortBy === DEFAULT_SORT_CONFIG.sortBy && sortOrder === DEFAULT_SORT_CONFIG.sortOrder;
    -setSortConfig((prevState) => ({...prevState, sortBy: selectedSortBy, sortOrder: selectedSortOrder}));
    +const isReturningToDefaultColumn = selectedSortBy !== sortBy && selectedSortBy === DEFAULT_SORT_CONFIG.sortBy;
    +setSortConfig((prevState) => ({...prevState, sortBy: selectedSortBy, sortOrder: isReturningToDefaultColumn ? DEFAULT_SORT_CONFIG.sortOrder : selectedSortOrder}));

    Only the third hunk changes behaviour. I hoist rather than add a fourth DATE/ASC literal because the drift between the two places that spell the default out and the one that did not is the bug.

    Regression safety:

    • Search is untouched. Same column, opposite default, and Search writes the emitted order into the URL query, so changing the shared header would re-rank saved and shared search links:
      const onSortPress = useCallback(
      (column: SearchSortBy, order: SortOrder) => {
      clearSelectedTransactions();
      const newQuery = buildSearchQueryString({
      ...queryJSON,
      sortBy: column,
      sortOrder: order,
      });
      onSortPressedCallback?.();
      navigation.setParams({q: newQuery, rawQuery: undefined});
      },
      [clearSelectedTransactions, queryJSON, onSortPressedCallback, navigation],
    • setSortConfig has exactly one call site, the handler above.
    • Toggling the active Date column is unaffected: selectedSortBy !== sortBy is false, so repeat clicks still flip ASC↔DESC. Switching to any non-default column still lands on DESC.
    • No offline or optimistic path is involved: sortConfig is local useState, with no API call and no failureData.
    • The grouped layout derives from sortedTransactions, so it inherits the corrected order with no extra change.

    One limitation I want to be straight about: a user who deliberately sets Date/DESC, switches to Amount and comes back to Date gets ASC, not the DESC they last chose. The view has one default and this makes it reachable in one click; a second click still gives DESC.

    Tests, all of which I ran:

    1. Steps 5 → 7 → 8: open, click Amount, click Date, expect date/asc and isDefaultSort true. Fails on current main with the output above, passes with the fix.
    2. Date → Date still toggles DESC then ASC. Passes on main and with the fix.
    3. Merchant and Amount still land on DESC. Passes on main and with the fix.
    4. MoneyRequestReportTransactionListActiveTransactionIDsTest, MoneyRequestReportViewTest, MoneyRequestReportGroupHeaderTest — 15 tests, green with the fix applied.

    I did not render the whole transaction list in Jest. Test 1 drives the real MoneyRequestReportTableHeader → SortableTableHeader → SortableHeaderText chain and reproduces the onSortPress body verbatim.

    What alternative solutions did you explore? (Optional)

    • Flip the ternary to ASC. Rejected: Search's Date default is DESC, so this makes the shared header wrong for its other consumer, and Search's order is encoded in the URL.
    • A defaultSortOrder prop, my earlier proposal. Rejected above: per table rather than per column, so it changes five other columns in this table.
    • A deterministic tie-break in the comparator. Worth doing on its own, but it only makes equal rows stable; it cannot make Date land on ASC, so it does not address the reported default sort.
  7. MobileMage commented on May 28, 2026

    @MobileMage
    Contributor

    Proposal

    Please re-state the problem that we are trying to solve in this issue.

    In the money request report view (Spend → Reports → open a report), the initial default sort is Date/ASC so that RBR-flagged transactions are surfaced at the top. After clicking the Total column header to change sort, then clicking the Date column header again, Date sorts in DESC order instead of returning to the expected Date/ASC default. This contradicts step 8 of the test plan from the linked PR #91710 ("Switch back to Date/ASC and verify RBR-flagged item is back on the top") because clicking Date no longer restores the table's documented default ordering.

    What is the root cause of that problem?

    The READ side — MoneyRequestReportTransactionList — initialises sortConfig with {sortBy: DATE, sortOrder: ASC} and treats that combination as the "default sort" guard that triggers the RBR pre-sort path. The WRITE side — clicks coming up from SortableHeaderText — computes the next order with logic that does not know about this table's ASC default:

    const nextSortOrder = isActive && sortOrder === CONST.SEARCH.SORT_ORDER.DESC ? CONST.SEARCH.SORT_ORDER.ASC : CONST.SEARCH.SORT_ORDER.DESC;
    return (
    <View style={containerStyle}>
    <PressableWithFeedback
    onPress={() => onPress(nextSortOrder)}

    Line 60 reads const nextSortOrder = isActive && sortOrder === DESC ? ASC : DESC. When the user clicks a column that is not currently active (isActive is false because sortBy === 'total' after step 6, and Date is being re-clicked in step 7), the expression short-circuits to the falsy branch and returns DESC. The press handler in MoneyRequestReportTransactionList then writes that order straight into state without translating it to the table's per-column default:

    onSortPress={(selectedSortBy, selectedSortOrder) => {
    if (!isSortableColumnName(selectedSortBy)) {
    return;
    }
    setSortConfig((prevState) => ({...prevState, sortBy: selectedSortBy, sortOrder: selectedSortOrder}));
    }}

    The full chain is therefore: user clicks Total → SortableHeaderText emits ('total', DESC) → state becomes {sortBy: 'total', sortOrder: 'DESC'} (correct behaviour for non-default columns) → user clicks Date → SortableHeaderText sees isActive=false for Date and emits ('date', DESC) → state becomes {sortBy: 'date', sortOrder: 'DESC'}. Because isDefaultSort on line 291 only fires when sortOrder === ASC, the RBR pre-sort path in rbrTransactionIDs (lines 297-311) is also bypassed, so the RBR-flagged transaction no longer floats to the top either. The behaviour in SortableHeaderText is the right global default for the main Search screen, which uses DESC as its defaultSortOrder in SearchUIUtils (line 646), but it is wrong for this specific table whose default is Date/ASC. The mismatch was latent before PR #91710 — the new test plan added the Total → Date round-trip step, which is why staging surfaces it but production (which still ran the old test plan) does not.

    What changes do you think we should make in order to solve the problem?

    Fix it locally inside MoneyRequestReportTransactionList's onSortPress handler so the global SortableHeaderText keeps its existing behaviour for Search (which legitimately defaults to DESC). When the user clicks a column that is not the currently active sort column (i.e. selectedSortBy !== sortBy), use the table's per-column default: ASC when selectedSortBy === CONST.SEARCH.TABLE_COLUMNS.DATE, otherwise the incoming selectedSortOrder (DESC). When the user clicks the already-active column, honour selectedSortOrder so toggling Date between ASC and DESC still works.

    onSortPress={(selectedSortBy, selectedSortOrder) => {
    if (!isSortableColumnName(selectedSortBy)) {
    return;
    }
    setSortConfig((prevState) => ({...prevState, sortBy: selectedSortBy, sortOrder: selectedSortOrder}));
    }}

    Concretely, inside the existing onSortPress callback after the isSortableColumnName guard, compute const nextOrder = selectedSortBy !== sortBy && selectedSortBy === CONST.SEARCH.TABLE_COLUMNS.DATE ? CONST.SEARCH.SORT_ORDER.ASC : selectedSortOrder and pass that as sortOrder into setSortConfig. This restores the original Date/ASC default on switch-back, which in turn re-arms isDefaultSort on line 291 and re-enables the RBR pre-sort that PR #91710 wanted to validate. The change is scoped to a single handler and a single column, does not modify any shared helper or other Search consumers, and preserves the column-toggle behaviour for repeated clicks on Date (DESC ↔ ASC) because the selectedSortBy !== sortBy check is false on a re-click of the active column.

    What alternative solutions did you explore? (Optional)

    Adding a per-column default-order map to SortableHeaderText (so the global component computes ASC for Date and DESC for everything else) was considered but rejected — it would change behaviour for the main Search screen and every other consumer of SortableTableHeader, where DESC is the intentional default (defaultSortOrder = DESC in SearchUIUtils.ts). Keeping the fix local to the one table that has an ASC default avoids any cross-screen regressions.

  8. melvin-bot commented on May 28, 2026

    @melvin-bot

    Triggered auto assignment to Contributor-plus team member for initial proposal review - @rojiphil (External)

  9. changed the title [-]Spend - Default sort is descending[/-] [+][$250] Spend - Default sort is descending[/+] on May 28, 2026
  10. melvin-bot commented on May 28, 2026

    @melvin-bot
  11. github-actions commented on May 28, 2026

    @github-actions
    Contributor

    ⚠️ @yusufdeveloper2903 Thanks for your proposal. Please update it to follow the proposal template, as proposals are only reviewed if they follow that format (note the mandatory sections).

  12. github-actions commented on May 28, 2026

    @github-actions
    Contributor

    ⚠️ @MobileMage Your proposal is a duplicate of an already existing proposal and has been automatically withdrawn to prevent spam. Please review the existing proposals before submitting a new one.

  13. 156 remaining items

  14. b4fpj6jrzf-boop commented on Oct 17, 2026

    @b4fpj6jrzf-boop

    Proposal

    Please re-state the problem that we are trying to solve in this issue.

    When sorting reports or data tables in the app (e.g. Spend -> Reports), clicking on a non-active column header defaults to sorting descending (DESC) instead of ascending (ASC). The expected behavior across standard tables is that clicking an unselected column should sort it in ascending order first, and subsequent clicks on the same active column toggle between ascending and descending.

    What is the root cause of that problem?

    In src/components/SortableHeaderText.tsx, line 60:

    const nextSortOrder = isActive && sortOrder === CONST.SORT_ORDER.DESC ? CONST.SORT_ORDER.ASC : CONST.SORT_ORDER.DESC;

    When an inactive column header is clicked, isActive is false. Because of the isActive && ... expression, it evaluates to false and automatically falls into the falsy ternary branch, returning CONST.SORT_ORDER.DESC.

    What changes do you think we should make in order to solve the problem?

    We should separate the condition so that if the column is NOT currently active (!isActive), the next sort order must default to ASC. Only if the column IS active (isActive) should we toggle between ASC and DESC:

    In src/components/SortableHeaderText.tsx:

    - const nextSortOrder = isActive && sortOrder === CONST.SORT_ORDER.DESC ? CONST.SORT_ORDER.ASC : CONST.SORT_ORDER.DESC;
    + const nextSortOrder = isActive ? (sortOrder === CONST.SORT_ORDER.DESC ? CONST.SORT_ORDER.ASC : CONST.SORT_ORDER.DESC) : CONST.SORT_ORDER.ASC;

    Or cleanly written using an early return/ternary:

    const nextSortOrder = isActive 
        ? (sortOrder === CONST.SORT_ORDER.DESC ? CONST.SORT_ORDER.ASC : CONST.SORT_ORDER.DESC) 
        : CONST.SORT_ORDER.ASC;

    What alternative solutions did you explore? (Optional)

    None, fixing the ternary logic directly in SortableHeaderText.tsx is the cleanest, most performant, and root-cause fix without affecting any parent component states.

    Unit Test / Regression Test Plan

    In tests/unit/SortableHeaderTextTest.tsx:

    1. Render SortableHeaderText with isActive={false} and sortOrder={CONST.SORT_ORDER.DESC}.
    2. Press the header and verify onPress is called with CONST.SORT_ORDER.ASC.
    3. Render with isActive={true} and sortOrder={CONST.SORT_ORDER.ASC}.
    4. Press and verify onPress is called with CONST.SORT_ORDER.DESC.
    5. Render with isActive={true} and sortOrder={CONST.SORT_ORDER.DESC}.
    6. Press and verify onPress is called with CONST.SORT_ORDER.ASC.
  15. melvin-bot commented on Oct 17, 2026

    @melvin-bot

    📣 @b4fpj6jrzf-boop! 📣
    Hey, it seems we don’t have your contributor details yet! You'll only have to do this once, and this is how we'll hire you on Upwork.
    Please follow these steps:

    1. Make sure you've read and understood the contributing guidelines.
    2. Get the email address used to login to your Expensify account. If you don't already have an Expensify account, create one here. If you have multiple accounts (e.g. one for testing), please use your main account email.
    3. Get the link to your Upwork profile. It's necessary because we only pay via Upwork. You can access it by logging in, and then clicking on your name. It'll look like this. If you don't already have an account, sign up for one here.
    4. Copy the format below and paste it in a comment on this issue. Replace the placeholder text with your actual details.
      Screen Shot 2022-11-16 at 4 42 54 PM
      Format:
    Contributor details
    Your Expensify account email: <REPLACE EMAIL HERE>
    Upwork Profile Link: <REPLACE LINK HERE>
    
  16. b4fpj6jrzf-boop commented on Oct 17, 2026

    @b4fpj6jrzf-boop
  17. melvin-bot commented on Oct 17, 2026

    @melvin-bot

    ⚠️ Invalid email. Please make sure to create an Expensify account with this email first here.

  18. github-actions commented on Oct 17, 2026

    @github-actions
    Contributor

    ⚠️ @b4fpj6jrzf-boop Thanks for your interest. To be considered for this job, please post a proposal following the proposal template (note the mandatory sections). Comments claiming the job without one are not reviewed.

  19. b4fpj6jrzf-boop commented on Oct 17, 2026

    @b4fpj6jrzf-boop
  20. melvin-bot commented on Oct 17, 2026

    @melvin-bot

    ⚠️ Invalid email. Please make sure to create an Expensify account with this email first here.

  21. github-actions commented on Oct 17, 2026

    @github-actions
    Contributor

    ⚠️ @b4fpj6jrzf-boop Thanks for your interest. To be considered for this job, please post a proposal following the proposal template (note the mandatory sections). Comments claiming the job without one are not reviewed.

  22. b4fpj6jrzf-boop commented on Oct 17, 2026

    @b4fpj6jrzf-boop
  23. melvin-bot commented on Oct 17, 2026

    @melvin-bot

    ✅ Contributor details stored successfully. Thank you for contributing to Expensify!

  24. github-actions commented on Oct 17, 2026

    @github-actions
    Contributor

    ⚠️ @b4fpj6jrzf-boop Thanks for your interest. To be considered for this job, please post a proposal following the proposal template (note the mandatory sections). Comments claiming the job without one are not reviewed.

  25. open5588 commented on Oct 19, 2026

    @open5588

    Proposal

    Please re-state the problem that we are trying to solve in this issue.

    In Spend / Reports tables, clicking an unselected column header (such as switching from Date to Total and back to Date) always sorts in descending (DESC) order instead of restoring the column default (ASC), and when transactions are grouped by Category or Tag, changing the sort order does not reorder the groups.

    What is the root cause of that problem?

    1. In src/components/Search/SortableHeaderText.tsx, clicking an inactive column (!isActive) always computes nextSortOrder = CONST.SEARCH.SORT_ORDER.DESC. useMoneyRequestReportSortedTransactions.ts stores that emitted DESC order directly even though the table initializes Date sort as ASC.
    2. In src/libs/ReportLayoutUtils.ts, groupTransactionsByCategory and groupTransactionsByTag sort transactions first but then sort the resulting groups alphabetically by group key, which overrides the transaction sort order when each group contains a single transaction.

    What changes do you think we should make in order to solve the problem?

    1. In src/components/MoneyRequestReportView/useMoneyRequestReportSortedTransactions.ts (and SortableHeaderText.tsx via an optional defaultSortOrder prop), default newly selected columns in the report table to CONST.SEARCH.SORT_ORDER.ASC on first click and toggle between ASC and DESC when clicking the already-active column.
    2. In src/libs/ReportLayoutUtils.ts, when an explicit sortBy / sortOrder is active on the report table, order the groups by their first (already sorted) transaction rather than re-sorting the groups alphabetically.

    What alternative solutions did you explore? (Optional)

    Changing SortableHeaderText globally for all Search views could alter main Search page defaults where Date DESC is expected, so scoping the default ASC behavior via a prop passed by MoneyRequestReportTransactionList avoids regressions in global Search.


    Contributor details
    Your Expensify account email: open5588@gmail.com
    Upwork Profile Link: https://www.upwork.com/freelancers/~0159c00273dd3bd644

  26. melvin-bot commented on Oct 19, 2026

    @melvin-bot

    ✅ Contributor details stored successfully. Thank you for contributing to Expensify!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

BugSomething is broken. Auto assigns a BugZero manager.DailyKSv2ExternalAdded to denote the issue can be worked on by a contributorHelp WantedApply this label when an issue is open to proposals by contributorsOverdue

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions