Skip to content

Category - Parent category is separated from subcategories when searched #85364

Closed

Description

@jponikarchuk

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.38-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: #83200
Email or phone of affected tester (no customers): sdjsdjosijnsodiosd@gmail.com
Issue reported by: Applause Internal Team
Bug source: Exploratory - Significant User Experience Deterioration
Device used: Mac 26.2 / Chrome
App Component: Money Requests

Action Performed:

Precondition:

  • Create both categories - D and D:E:F
  1. Go to staging.new.expensify.com
  2. Go to workspace chat.
  3. Click + > Create expense > Manual.
  4. Enter amount > Next.
  5. Click Category.
  6. Select D.
  7. Click Category.
  8. On the search field, type D.

Expected Result:

Parent category (D) and subcategories (E and F) will be arranged together.

Actual Result:

Parent category (D) and subcategories (E and F) are separated by Advertising category.

Workaround:

Unknown

Platforms:

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

Screenshots/Videos

Video.mp4

View all open jobs on GitHub

Activity

  1. added
    DeployBlockerCashThis issue or pull request should block deployment
    BugSomething is broken. Auto assigns a BugZero manager.
    on Mar 16, 2026
  2. melvin-bot commented on Mar 16, 2026

    @melvin-bot

    You have been assigned to this deploy blocker because you recently merged this PR: #83200

  3. melvin-bot commented on Mar 16, 2026

    @melvin-bot

    💬 A slack conversation has been started in #expensify-open-source

  4. github-actions commented on Mar 16, 2026

    @github-actions
    Contributor

    👋 Friendly reminder that deploy blockers are time-sensitive ⏱ issues! Check out the open `StagingDeployCash` deploy checklist to see the list of PRs included in this release, then work quickly to do one of the following:

    1. Identify the pull request that introduced this issue and revert it.
    2. Find someone who can quickly fix the issue.
    3. Fix the issue yourself.
  5. OSBotify commented on Mar 16, 2026

    @OSBotify
    Contributor

    🔍 Investigation Summary

    Classification: Frontend bug
    Causing PR: #83200 - "Multi-level categories with colons are shown with hierarchical indentation" by @ijmalik (High confidence)
    Related Issues: #82068 (original issue that PR #83200 was fixing)

    Recommendation: REVERT

    PR #83200 should be reverted. While it successfully added hierarchical indentation for category display, it inadvertently broke the search functionality by removing the flat one-line display mode that was previously used for search results.

    Labels: No label changes needed - keeping DeployBlockerCash as this is a frontend App bug that requires an App PR revert/fix.

    📋 Detailed Analysis

    Evidence

    PR #83200 modified src/libs/CategoryOptionListUtils.ts to add hierarchical indentation for multi-level categories. The PR removed the isOneLine parameter from the getCategoryOptionTree() function, which previously allowed categories to be displayed in two modes:

    1. Hierarchical mode (with indentation) - for normal category list display
    2. One-line mode (flat) - for search results

    After the PR, only hierarchical mode exists, which is now used for ALL displays including search results.

    Verification

    The files modified by PR #83200:

    • src/CONST/index.ts - Changed PARENT_CHILD_SEPARATOR from ": " to ":"
    • src/libs/CategoryOptionListUtils.ts - Removed isOneLine parameter and one-line display logic
    • tests/unit/CategoryOptionListUtilsTest.ts - Updated tests

    The bug occurs in the search flow (lines 119-135 of CategoryOptionListUtils.ts):

    1. User searches for "D"
    2. tokenizedSearch returns matching categories: "D", "D:E:F", "Advertising"
    3. getCategoryOptionTree now always uses hierarchical mode
    4. It creates separate entries for parent "D" and children "D:E", "D:E:F"
    5. Alphabetical sorting places "Advertising" between "D" and "D:E"
    6. Result: Parent and subcategories are separated

    Root Cause

    The PR assumed hierarchical indentation should be used everywhere, but search results need a flat display to keep parent categories together with their children. When hierarchical mode is used with alphabetical sorting, unrelated categories can appear between a parent and its subcategories.

    Before PR #83200: Search used flat one-line mode

    • D
    • D:E:F
    • Advertising

    After PR #83200: Search uses hierarchical mode with alphabetical sorting

    • D (parent)
    • Advertising (alphabetically between D and D:E)
    • E (child of D, displayed as D:E)
    • F (child of D:E, displayed as D:E:F)
  6. MelvinBot commented on Mar 16, 2026

    @MelvinBot
    Contributor

    Proposal

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

    When a user selects a parent category (e.g. "D") and then searches for it, the parent category and its subcategories (from "D:E:F") are separated by unrelated categories like "Advertising" in the search results. They should be grouped together.

    What is the root cause of that problem?

    The bug is in getCategoryListSections in src/libs/CategoryOptionListUtils.ts:120. When searching with a selected category, the code prepends selectedOptionsWithDisabledState before enabledCategories:

    const categoriesForSearch = [...selectedOptionsWithDisabledState, ...enabledCategories];
    

    enabledCategories comes from sortCategories which maintains correct hierarchical order: [Advertising, D, D:E:F]. But prepending the selected "D" creates: [D(selected), Advertising, D(duplicate), D:E:F].

    tokenizedSearch preserves this order (all items match "d" via substring — including "Advertising"). Then getCategoryOptionTree iterates the results and uses a Map for insertion-order tracking. It processes:

    1. "D" (selected) → inserts into Map
    2. "Advertising" → inserts after D
    3. "D" (duplicate) → already in Map, skipped
    4. "D:E:F" → "D" already exists (skip), inserts "D:E" and "D:E:F" after Advertising

    Final display order: D, Advertising, E, F — subcategories are separated from their parent.

    This is a regression from PR #83200 which introduced hierarchical splitting of colon-separated categories. Before that PR, "D:E:F" was displayed as a single flat item, so the prepend ordering didn't cause visual separation.

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

    In the search block of getCategoryListSections (src/libs/CategoryOptionListUtils.ts, lines 119–136), after tokenizedSearch returns filtered results, deduplicate by name and re-sort through sortCategories to restore hierarchical grouping. Then re-apply isSelected (since sortCategories doesn't preserve that property). This ensures parent categories always stay adjacent to their subcategories in search results regardless of selected state.

    What alternative solutions did you explore? (Optional)

    1. Remove the prepend entirely — Don't include selectedOptionsWithDisabledState in categoriesForSearch. This would fix the ordering but could break the case where a selected-but-disabled category should still appear in search results.
    2. Deduplicate without re-sorting — Filter duplicates from enabledCategories before prepending. This prevents the duplicate "D" but still leaves selected "D" before Advertising, so subcategories from "D:E:F" would still appear after Advertising. Doesn't fully fix the issue.

    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.

  7. melvin-bot commented on Mar 16, 2026

    @melvin-bot

    ⚠️ Looks like this issue was linked to a Deploy Blocker here

    If you are the assigned CME please investigate whether the linked PR caused a regression and leave a comment with the results.

    If a regression has occurred and you are the assigned CM follow the instructions here.

    If this regression could have been avoided please consider also proposing a recommendation to the PR checklist so that we can avoid it in the future.

  8. IuliiaHerets commented on Mar 16, 2026

    @IuliiaHerets

    Chrome / v9.3.38-1
    Issue is still reproduce

    Screen.Recording.2026-03-16.at.21.55.37.mp4
  9. yuwenmemon commented on Mar 16, 2026

    @yuwenmemon
    Contributor

    @IuliiaHerets are you sure this is actually still reproducible? This behavior looks very different than the original screen recording.

  10. IuliiaHerets commented on Mar 16, 2026

    @IuliiaHerets

    @yuwenmemon you are right, the issue is not reproduced.
    Sorry for confusing

    Image
  11. yuwenmemon commented on Mar 16, 2026

    @yuwenmemon
    Contributor

    No worries at all - thanks! Closing

  12. lanitochka17 commented on May 19, 2026

    @lanitochka17

    Deploy Blocker ##91133 was identified to be related to the PR ##85448 that attempted to fix this issue.

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.EngineeringHourlyKSv2

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions