Repository navigation
Description
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: N/A
Reproducible in staging?: Yes
Reproducible in production?: Yes
If this was caught during regression testing, add the test name, ID and link from BrowserStack: N/A
Email or phone of affected tester (no customers): N/A
Logs: N/A
Expensify/Expensify Issue URL: N/A
Issue reported by: @brianlee-expensify
Slack conversation: https://expensify.slack.com/archives/C05LX9D6E07/p1770752446067979
Action Performed:
- Create or use a workspace with multi-level categories that use colons as separators (e.g.
Primary:Secondary:Tertiary) - Open an expense and view the category selector in NewDot
Expected Result:
Multi-level categories should be displayed with hierarchical indentation (like in Classic/OldDot), where sub-categories are visually nested under their parent categories. For example:
Primary
Secondary
Tertiary
Professional Services
Actual Result:
Categories are displayed as flat, colon-separated strings (e.g. Primary:Secondary:Tertiary), making them hard to read — especially for long or deeply nested category names. The full text is also difficult to read at the individual expense level because the display area is too narrow for long concatenated names.
In Classic/OldDot, the dropdown properly parses the colons and renders sub-categories with indentation, which is significantly more readable.
Workaround:
Users can hover over each option in NewDot to see the full text, but this is far less efficient than the Classic hierarchical display.
Platforms:
- Android: App
- Android: mWeb Chrome
- iOS: App
- iOS: mWeb Safari
- iOS: mWeb Chrome
- Windows: Chrome
- MacOS: Chrome / Safari
Screenshots/Videos
NewDot (current — flat colon-separated display):
Long category names like General and Administrative Expenses/Other Ge... are truncated and hard to read.

Classic/OldDot (expected — hierarchical indentation):
Categories with colons are parsed and displayed with visual nesting:
Primaryshown at root levelSecondaryindented under PrimaryTertiaryfurther indented under Secondary
cc @JmillsExpensify @puneetlath @dannymcclain @shawnborton
Issue Owner
Current Issue Owner: @yuwenmemonUpwork Automation - Do Not Edit
- Upwork Job URL: https://www.upwork.com/jobs/~022021404126598547656
- Upwork Job ID: 2021404126598547656
- Last Price Increase: 2026-02-18
- Automatic offers:
- ijmalik | Contributor | 110649720
Activity
melvin-bot commentedon Feb 11, 2026
Triggered auto assignment to @heyjennahay (
Bug), see https://stackoverflow.com/c/expensify/questions/14418 for more details. Please add this bug to a GH project, as outlined in the SO.melvin-bot commentedon Feb 11, 2026
Triggered auto assignment to Contributor-plus team member for initial proposal review - @ZhenjaHorbach (
External)[-]Multi-level categories/tags with colons are not displayed with hierarchical indentation in NewDot[/-][+][$250] Multi-level categories/tags with colons are not displayed with hierarchical indentation in NewDot[/+]melvin-bot commentedon Feb 11, 2026
Job added to Upwork: https://www.upwork.com/jobs/~022021404126598547656
yifangoh commentedon Feb 11, 2026
Proposal
Please re-state the problem that we are solving in this issue in your own words.
When multi-level categories use colons as separators (e.g.,
Primary:Secondary:Tertiary), they are displayed as flat concatenated strings in NewDot's category selector. This makes deeply nested categories hard to read, especially when names are long and get truncated. Classic/OldDot correctly parses the colons and displays sub-categories with visual indentation.What is the root cause of that problem?
The category selector renders category names as-is without parsing the colon delimiter to determine hierarchy depth. There's no logic to split on
:and apply indentation styling based on the resulting segments.What changes do you think we should make in order to solve the problem?
src/components/OptionsListor the category-specific selector), split the category name by:to determine the hierarchy levelPrimary:Secondary= depth 2)For example:
What alternative solutions did you explore? (Optional)
mukhrr commentedon Feb 11, 2026
Proposal
Please re-state the problem that we are trying to solve in this issue.
Multi-level categories/tags with colons are not displayed with hierarchical indentation - flat text without tree-like shape
What is the root cause of that problem?
The
PARENT_CHILD_SEPARATORconst is defined as:(colon with a trailing space) atsrc/CONST/index.ts.App/src/CONST/index.ts
Line 5718 in 04c98c9
The
getCategoryOptionTree()function insrc/libs/CategoryOptionListUtils.tssplits category names using this separator:App/src/libs/CategoryOptionListUtils.ts
Line 56 in 04c98c9
When categories come from accounting integrations (e.g., QuickBooks), they use
:without a space — e.g., "Primary:Secondary:Tertiary". Since"Primary:Secondary:Tertiary".split(': ')produces ["Primary:Secondary:Tertiary"] (a single-element array), no hierarchy is detected and no indentation is applied. The same issue affects the sortCategories() function which also splits by ': '.App/src/libs/CategoryOptionListUtils.ts
Lines 241 to 242 in 04c98c9
OldDot/Classic handles this correctly because it parses
:as the separator regardless of spacing.What changes do you think we should make in order to solve the problem?
We need to replace the literal
CONST.PARENT_CHILD_SEPARATORsplit with a regex/\s*:\s*/that handles both:and:and add asplitCategoryName(name)helper that splits by/\s*:\s*/and provides agetSearchText(index, isLeaf)method. For leaf entries it returns the originaloption.name(preserving the backend key for matching). For parent entries it finds theNthcolon position in the original string and returnssubstring(0, colonPos).trim(), preserving the original separator style in the key.Also in
sortCategories:What alternative solutions did you explore? (Optional)
HiepPP commentedon Feb 11, 2026
Proposal for Issue #82068
Problem
Multi-level categories that use colons as separators (e.g.,
Primary:Secondary:Tertiary) are displayed as flat, colon-separated strings in the category picker instead of with hierarchical indentation. This affects:The "All" categories section correctly displays hierarchical indentation.
Root Cause
The issue is in
src/libs/CategoryOptionListUtils.ts. ThegetCategoryOptionTree()function has anisOneLineparameter that controls whether to build the tree hierarchy with indentation:isOneLine = true: The function skips tree building and returns the full category name without indentationisOneLine = false: The function properly parses the colon separator and builds indented display namesCurrently,
getCategoryListSections()passesisOneLine = truefor:Only the "All" section (lines 171, 204) correctly passes
isOneLine = false.Root Cause Verification Checklist
isOneLineparameter directly controls whether indentation is appliedisOneLine = falsegetCategoryOptionTree()ingetCategoryListSections()are affectedMinimal Fix Checklist
Working vs Broken Comparison
isOneLine = falseisOneLine = trueAlternative Solutions Considered
Alternative 1: Modify
getCategoryOptionTree()to always build treeRemove the
isOneLineparameter entirely and always build the tree hierarchy.Why rejected: The
isOneLineparameter exists for a reason - when searching, users want to see the full category path to distinguish between similarly named subcategories. Changing this behavior globally could break other use cases.Alternative 2: Add new parameter for indentation only
Add a new parameter like
shouldIndentthat controls indentation separately from tree building.Why rejected: This adds complexity without benefit. The existing
isOneLineflag is meant to control both tree building and indentation display. The simpler fix is to passfalsewhere we want indentation.Alternative 3: Post-process in RadioListItem
Handle indentation in the UI component instead of the data layer.
Why rejected: This would require passing additional metadata to the component and would be a more invasive change. The current approach of preparing data in
CategoryOptionListUtilsis the established pattern.Solution
Change
isOneLinefromtruetofalsefor the three broken flows ingetCategoryListSections():This ensures all category displays use the same hierarchical indentation logic.
Implementation
Files Modified
src/libs/CategoryOptionListUtils.tsKey Changes
Testing Approach
Primary:Secondary:Tertiary)Edge Cases
Performance Impact
Minimal. The tree building logic is already used for the "All" section and performs well. The additional processing for selected/recent categories (typically small sets) will have negligible impact.
72 remaining items
brianlee-expensify commentedon Jun 4, 2026
friendly bump for @yuwenmemon !
ijmalik commentedon Jun 9, 2026
Another friendly bump for @yuwenmemon and @ZhenjaHorbach when you get a quick moment to check the payment tracker issue. Appreciate it!
CC: @brianlee-expensify
melvin-bot commentedon Jun 12, 2026
@yuwenmemon Uh oh! This issue is overdue by 2 days. Don't forget to update your issues!
ZhenjaHorbach commentedon Jun 15, 2026
BugZero Checklist:
It's a new feature
[Contributor] If the regression was CRITICAL (e.g. interrupts a core flow) A discussion in #expensify-open-source has been started about whether any other steps should be taken (e.g. updating the PR review checklist) in order to catch this type of bug sooner.
Link to discussion: NA
[Contributor] If it was decided to create a regression test for the bug, please propose the regression test steps using the template below to ensure the same bug will not reach production again.
Regression Test Proposal
Test:
Do we agree 👍 or 👎
Zapier Logs
Run ID: 00040eee-9b84-a64e-4e09-50f095b761f4ZhenjaHorbach commentedon Jun 15, 2026
Oh
Sorry
I missed this issue
And about payment
We had a few regressions
But this issue was quite complex, with a lot of edge cases
Plus, we expanded the scope of the issue, which required significant time for testing
#85448 (comment)
So, if you don't mind, I propose 375$ here
CC: @trjExpensify(Since you proposed updating the UI in a similar way to the OD 😁)
trjExpensify commentedon Jun 16, 2026
Works for me. 👍
ZhenjaHorbach commentedon Jun 16, 2026
@mallenexpensify
We are done here!
mallenexpensify commentedon Jun 16, 2026
Payment Summary
Contributor: @ijmalik paid $375 via Upwork
Contributor+: @ZhenjaHorbach due $375 via NewDot
^ Test case created. Thx
garrettmknight commentedon Jun 18, 2026
$375 approved for @ZhenjaHorbach