Skip to content

[HOLD for #98030] Move Categories & Tags settings into the header More menu - #100293

Open
marufsharifi wants to merge 12 commits into
Expensify:mainfrom
marufsharifi:categories-tags-header-menu
Open

marufsharifi wants to merge 12 commits into
Expensify:mainfrom
marufsharifi:categories-tags-header-menu

Conversation

@marufsharifi

@marufsharifi marufsharifi commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Explanation of Change

Behind the rulesRevamp beta, the per-page settings on the workspace Categories and Tags pages now live directly in the header More menu instead of a separate Settings RHP:

  • Categories — adds the Show GL codes when categorizing expenses toggle row.
  • Tags — adds the Custom tag name row (current value + chevron, opens the existing RHP) and the Show GL codes when selecting a tag toggle row.
  • Toggles flip in place and keep the menu open; the divider groups the settings rows above Import spreadsheet / Download CSV.

When the beta is off, the existing Settings pages are kept unchanged as a fallback.

Fixed Issues

$ #99886
PROPOSAL:

Tests

Prerequisites: rulesRevamp beta enabled, and a Control-plan workspace (or one with GL codes) to see the GL-code rows.

Categories

  1. Go to Workspace → Categories → More. The menu should display:

    • Show GL codes when categorizing expenses — toggle
    • Divider
    • Import spreadsheet
    • Download CSV

    There should be no Settings option.

  2. Turn on or off Show GL codes when categorizing expenses.

    • The change is saved immediately.
    • The More menu remains open after toggling.

Tags

  1. Go to Workspace → Tags → More. The menu should display:

    • Custom tag name — current value with a chevron
    • Show GL codes when selecting a tag — toggle
    • Divider
    • Import spreadsheet
    • Download CSV

    There should be no Settings option.

  2. Tap Custom tag name.

    • The More menu closes.
    • The existing right-hand panel (RHP) opens.
    • Enter and save a new name.
    • After saving, return to Tags.
    • The Custom tag name row displays the newly saved value.
  3. Turn on or off Show GL codes when selecting a tag.

    • The change is saved immediately.
    • The More menu remains open after toggling.
  • Verify that no errors appear in the JS console

Offline tests

QA Steps

// TODO: These must be filled out, or the issue title must include "[No QA]."

  • Verify that no errors appear in the JS console

PR Author Checklist

  • I linked the correct issue in the ### Fixed Issues section above
  • I wrote clear testing steps that cover the changes made in this PR
    • I added steps for local testing in the Tests section
    • I added steps for the expected offline behavior in the Offline steps section
    • I added steps for Staging and/or Production testing in the QA steps section
    • I added steps to cover failure scenarios (i.e. verify an input displays the correct error message if the entered data is not correct)
    • I turned off my network connection and tested it while offline to ensure it matches the expected behavior (i.e. verify the default avatar icon is displayed if app is offline)
    • I tested this PR with a High Traffic account against the staging or production API to ensure there are no regressions (e.g. long loading states that impact usability).
  • I included screenshots or videos for tests on all platforms
  • I ran the tests on all platforms & verified they passed on:
    • Android: Native
    • Android: mWeb Chrome
    • iOS: Native
    • iOS: mWeb Safari
    • MacOS: Chrome / Safari
  • I verified there are no console errors (if there's a console error not related to the PR, report it or open an issue for it to be fixed)
  • I followed proper code patterns (see Reviewing the code)
    • I verified that comments were added to code that is not self explanatory
    • I verified that any new or modified comments were clear, correct English, and explained "why" the code was doing something instead of only explaining "what" the code was doing.
    • I verified any copy / text that was added to the app is grammatically correct in English. It adheres to proper capitalization guidelines (note: only the first word of header/labels should be capitalized), and is either coming verbatim from figma or has been approved by marketing (in order to get marketing approval, ask the Bug Zero team member to add the Waiting for copy label to the issue)
  • If a new code pattern is added I verified it was agreed to be used by multiple Expensify engineers
  • I followed the guidelines as stated in the Review Guidelines
  • I tested other components that can be impacted by my changes (i.e. if the PR modifies a shared library or component like Avatar, I verified the components using Avatar are working as expected)
  • If a new CSS style is added I verified that:
    • A similar style doesn't already exist
    • The style can't be created with an existing StyleUtils function (i.e. StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))
  • If new assets were added or existing ones were modified, I verified that:
    • The assets are optimized and compressed (for SVG files, run npm run compress-svg)
    • The assets load correctly across all supported platforms.
  • If the PR modifies code that runs when editing or sending messages, I tested and verified there is no unexpected behavior for all supported markdown - URLs, single line code, code blocks, quotes, headings, bold, strikethrough, and italic.
  • If the PR modifies a generic component, I tested and verified that those changes do not break usages of that component in the rest of the App (i.e. if a shared library or component like Avatar is modified, I verified that Avatar is working as expected in all cases)
  • If the PR modifies a component related to any of the existing Storybook stories, I tested and verified all stories for that component are still working as expected.
  • If the PR modifies a component or page that can be accessed by a direct deeplink, I verified that the code functions as expected when the deeplink is used - from a logged in and logged out account.
  • If the PR modifies the UI (e.g. new buttons, new UI components, changing the padding/spacing/sizing, moving components, etc) or modifies the form input styles:
    • I verified that all the inputs inside a form are aligned with each other.
    • I added Design label and/or tagged @Expensify/design so the design team can review the changes.
  • I added unit tests for any new feature or bug fix in this PR to help automatically prevent regressions in this user flow.
  • If the main branch was merged into this PR after a review, I tested again and verified the outcome was still expected according to the Test steps.

Screenshots/Videos

Android: Native

Categories

Screen.Recording.2026-09-04.at.6.46.40.PM.mov

Tags

Screen.Recording.2026-09-04.at.6.47.05.PM.mov
Android: mWeb Chrome

Categories

Recording_20260904_184053.mp4

Tags

Recording_20260904_184137.mp4
iOS: Native

Categories

Screen.Recording.2026-09-04.at.6.55.52.PM.mov

Tags

Screen.Recording.2026-09-04.at.6.56.16.PM.mov
iOS: mWeb Safari

Categories

Screen.Recording.2026-09-04.at.6.57.17.PM.mov

Tags

Screen.Recording.2026-09-04.at.6.58.35.PM.mov
MacOS: Chrome / Safari

Categories

Screen.Recording.2026-09-04.at.6.38.00.PM.mov

Tags

Screen.Recording.2026-09-04.at.6.41.11.PM.mov

@codecov

codecov Bot commented Sep 3, 2026

Copy link
Copy Markdown

Codecov Report

❌ Looks like you've decreased code coverage for some files. Please write tests to increase, or at least maintain, the existing level of code coverage. See our documentation here for how to interpret this table.

Files with missing lines Coverage Δ
src/ROUTES.ts 27.88% <ø> (ø)
src/components/ButtonWithDropdownMenu/index.tsx 84.32% <100.00%> (+0.11%) ⬆️
src/components/Switch.tsx 91.89% <ø> (ø)
src/components/PopoverMenu/index.tsx 92.20% <0.00%> (-0.86%) ⬇️
...s/workspace/categories/WorkspaceCategoriesPage.tsx 75.64% <28.57%> (-1.90%) ⬇️
src/pages/workspace/tags/WorkspaceTagsPage.tsx 61.81% <33.33%> (-3.58%) ⬇️
... and 10 files with indirect coverage changes

@marufsharifi
marufsharifi marked this pull request as ready for review September 4, 2026 14:39
@marufsharifi
marufsharifi requested review from a team as code owners September 4, 2026 14:39
@melvin-bot
melvin-bot Bot requested review from huult and removed request for a team September 4, 2026 14:39
@melvin-bot

melvin-bot Bot commented Sep 4, 2026

Copy link
Copy Markdown

@huult Please copy/paste the Reviewer Checklist from here into a new comment on this PR and complete it. If you have the K2 extension, you can simply click: [this button]

@melvin-bot
melvin-bot Bot requested review from heyjennahay and removed request for a team September 4, 2026 14:39
addSeparatorBefore?: boolean;

/** A component rendered on the right side of the option, e.g. a Switch for an inline toggle row */
rightComponent?: ReactNode;

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.

❌ CLEAN-REACT-PATTERNS-1 (docs)

Adding a named rightComponent?: ReactNode slot prop (together with its companion shouldShowRightComponent?: boolean flag) to DropdownOption is the ReactNode-slot-prop configuration anti-pattern. The options are .map()'d and spread into PopoverMenu/MenuItem (menuItems={options.map((item) => ({...item}))} in ButtonWithDropdownMenu/index.tsx), so every new positional slot widens a config-array-driven surface: the component must know about each slot and consumers cannot compose, reorder, or wrap it. This is exactly the illustrated bad example, which uses rightComponent?: ReactNode + shouldShowRightComponent.

Prefer composing the toggle row as an explicit child/compound-component slot rather than threading arbitrary JSX through the option config. For example, expose a dedicated menu-row component the consumer renders directly instead of passing rightComponent/shouldShowRightComponent in the option object:

<ButtonWithDropdownMenu.Menu>
    <ButtonWithDropdownMenu.ToggleRow
        title={translate('workspace.categories.showCategoryGLCodes')}
        isOn={policy?.showCategoryGLCodes ?? false}
        onToggle={(value) => setPolicyShowCategoryGLCodes(policyId, value)}
    />
</ButtonWithDropdownMenu.Menu>

so adding a new row type never requires expanding DropdownOption.


Reviewed at: 4927c16 | Please rate this suggestion with 👍 or 👎 to help us improve! Reactions are used to monitor reviewer efficiency.

menuItems.push({
text: translate('workspace.categories.showCategoryGLCodes'),
value: CONST.POLICY.SECONDARY_ACTIONS.SETTINGS,
// The row itself is inert; only the Switch handles the toggle so the menu stays open.

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.

❌ CONSISTENCY-16 (docs)

This comment joins two independent clauses with a semicolon. Comments should read as plain sentences, using two separate sentences instead of a semicolon.

Split it into two sentences:

// The row itself is inert. Only the Switch handles the toggle so the menu stays open.

Reviewed at: 4927c16 | Please rate this suggestion with 👍 or 👎 to help us improve! Reactions are used to monitor reviewer efficiency.

menuItems.push({
text: translate('workspace.tags.showTagGLCodes'),
value: CONST.POLICY.SECONDARY_ACTIONS.SETTINGS,
// The row itself is inert; only the Switch handles the toggle so the menu stays open.

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.

❌ CONSISTENCY-16 (docs)

This comment joins two independent clauses with a semicolon. Comments should read as plain sentences, using two separate sentences instead of a semicolon.

Split it into two sentences:

// The row itself is inert. Only the Switch handles the toggle so the menu stays open.

Reviewed at: 4927c16 | Please rate this suggestion with 👍 or 👎 to help us improve! Reactions are used to monitor reviewer efficiency.

@huult

huult commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

@marufsharifi Could you check the comments above?

@marufsharifi

Copy link
Copy Markdown
Contributor Author

@huult The feedbacks have been resolved, thanks

containerStyles={containerStyles}
menuItems={options.map((item, index) => ({
...item,
// Build the Switch here from typed data so callers pass `switchProps` instead of threading JSX through the option config.

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.

@marufsharifi Do we need a comment here? I think the code is clear enough to understand without it, so we can remove it.

Comment thread src/components/PopoverMenu/index.tsx Outdated
onFocus={() => {
if (!shouldUpdateFocusedIndex) {
// Inert rows (e.g. an inline toggle row whose only control is a Switch) shouldn't become the
// focused/highlighted item. Otherwise focus bubbling from the inner control leaves the row highlighted.

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.

@marufsharifi Do we need a comment here? I think the code is clear enough to understand without it, so we can remove it.

// Under the revamp the other settings moved to Rules, so this is only worth showing for the GL codes toggle.
if (canWriteCategories && (!isRulesRevampEnabled || !!policy?.glCodes)) {
// Under the revamp the Settings page is gone, so its remaining GL codes toggle is surfaced directly in this
// menu instead of behind a dedicated Settings page.

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.

@marufsharifi Do we need a comment here? I think the code is clear enough to understand without it, so we can remove it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@huult This comment already existed on main — I only updated its wording for the new placement. I'd keep it for context, but can remove if you prefer. thanks

const menuItems = [];
if (shouldShowTagsSettings) {
// Under the revamp the Settings page is gone, so its remaining rows (custom tag name and the GL codes toggle)
// are surfaced directly in this menu using the same visibility rules the Settings page used.

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.

@marufsharifi Do we need a comment here? I think the code is clear enough to understand without it, so we can remove it.

@huult

huult commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Auto-categorize new expenses is a brand new toggle that just hit. We'll get the mock updated, as it's now in scope.

Screenshot 2026-09-06 at 16 00 59

BUG: The "Auto-categorize new expenses" toggle is completely missing from Categories.

@huult

huult commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Removing the Settings page

@marufsharifi I didn't see you remove anything. Is this missing, or has it still not been removed?

@huult

huult commented Sep 6, 2026

Copy link
Copy Markdown
Contributor
Screen.Recording.2026-09-06.at.16.04.44.mov
Screen.Recording.2026-09-06.at.16.06.07.mov

BUG: The error message is not displayed, and the toggle silently reverts when simulating a network failure. (tags and categories)

@huult

huult commented Sep 6, 2026

Copy link
Copy Markdown
Contributor
Screenshot 2026-09-06 at 16 14 30 Screenshot 2026-09-06 at 16 15 00

Bug: the switch sits pinned to the top of the row instead of centered against the wrapped text.

@huult

huult commented Sep 6, 2026

Copy link
Copy Markdown
Contributor
Screen.Recording.2026-09-06.at.16.16.16.mov

BUG: When I press Enter on the toggle and it redirects to edit the tag name, the toggle value should still switch.

@huult

huult commented Sep 6, 2026

Copy link
Copy Markdown
Contributor
Screen.Recording.2026-09-06.at.16.18.50.mov

BUG: When I press Enter on a toggle without any action

@marufsharifi

Copy link
Copy Markdown
Contributor Author

Auto-categorize new expenses is a brand new toggle that just hit. We'll get the mock updated, as it's now in scope.

@huult
The Auto-categorize new expenses setting (from #98030) was reverted in #100217, so autoCategorizeNewExpenses / setPolicyAutoCategorizeNewExpenses don't exist on main

@marufsharifi

Copy link
Copy Markdown
Contributor Author

Removing the Settings page

@marufsharifi I didn't see you remove anything. Is this missing, or has it still not been removed?

@huult I kept that page on purpose for now. Right now, this feature works only with the rulesRevamp beta. When the beta is on, the Settings item is removed and the rows move to the More menu. When the beta is off, I kept the Settings page and routes as a fallback.
I did this because the Settings page still has some rows that are not shown anywhere else when the beta is off: Members must categorize / Members must tag, Track billable expenses, and the Default spend categories list. If I remove the page now, users without the beta will lose these rows.

Could you please clarify the timing? Should I remove the Settings page now, or later when this feature is out of beta and enabled for everyone? thanks

- Show the save error inside the menu row on failure instead of reverting
  silently: PopoverMenu now forwards each item's errors/onCloseError to its
  OfflineWithFeedback, and the GL-code rows pass errorFields + clearPolicyErrorField.
- Make the inline toggle rows keyboard-accessible: the row is interactive and
  toggles on select (click or Enter), and the Switch is rendered display-only
  (pointerEvents="none") so the mouse path doesn't double-toggle.
- Center the Switch against the (possibly wrapped) label via alignItemsCenter.
- Add errors/onCloseError to DropdownOption/PopoverMenuItem so a menu row can
@marufsharifi

Copy link
Copy Markdown
Contributor Author

@huult To fix the keyboard bugs, the toggle row is now a normal interactive menu item, so it highlights on hover/focus like the other rows (Import, Download).

REC-20260907183732.mp4

@heyjennahay Is that highlight acceptable, or should the toggle rows have no highlight? I want to confirm the expected design before adding extra handling to remove it while keeping keyboard access. thanks

@huult

huult commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Auto-categorize new expenses is a brand new toggle that just hit. We'll get the mock updated, as it's now in scope.

@huult The Auto-categorize new expenses setting (from #98030) was reverted in #100217, so autoCategorizeNewExpenses / setPolicyAutoCategorizeNewExpenses don't exist on main

@marufsharifi So, should we wait until they come back before adding “Auto-categorize new expenses” back in?

@huult

huult commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Removing the Settings page

@marufsharifi I didn't see you remove anything. Is this missing, or has it still not been removed?

@huult I kept that page on purpose for now. Right now, this feature works only with the rulesRevamp beta. When the beta is on, the Settings item is removed and the rows move to the More menu. When the beta is off, I kept the Settings page and routes as a fallback. I did this because the Settings page still has some rows that are not shown anywhere else when the beta is off: Members must categorize / Members must tag, Track billable expenses, and the Default spend categories list. If I remove the page now, users without the beta will lose these rows.

Could you please clarify the timing? Should I remove the Settings page now, or later when this feature is out of beta and enabled for everyone? thanks

Screenshot 2026-09-08 at 15 05 15

@marufsharifi Could you double-check whether we can remove it? The requirement is that it should be removed.

@marufsharifi

Copy link
Copy Markdown
Contributor Author

@marufsharifi Could you double-check whether we can remove it? The requirement is that it should be removed.

@huult I checked again — we can remove the Settings page only when the rulesRevamp beta is on. With the beta off, users still need it (those rows only move to Rules when the beta is on). So I plan to remove it (and redirect its routes to the list page) when the beta is on, and keep it as a fallback when the beta is off.

What do you think — does this work, or should I remove it fully now? Thanks!

@marufsharifi

Copy link
Copy Markdown
Contributor Author

@marufsharifi So, should we wait until they come back before adding “Auto-categorize new expenses” back in?

@huult Yes, I think we should wait until that setting is restored on main.
My suggestion: let's proceed with the rest of this PR, and I'll add the Auto-categorize new expenses row as soon as that setting re-lands. What do you think?

…pdate WorkspaceTagsPage to include pendingAction for policy tags
@huult

huult commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

@marufsharifi So, should we wait until they come back before adding “Auto-categorize new expenses” back in?

@huult Yes, I think we should wait until that setting is restored on main. My suggestion: let's proceed with the rest of this PR, and I'll add the Auto-categorize new expenses row as soon as that setting re-lands. What do you think?

@marufsharifi I think we should flag this issue, comment on the ticket, and let @JmillsExpensify decide.

@huult

huult commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

@marufsharifi Could you double-check whether we can remove it? The requirement is that it should be removed.

@huult I checked again — we can remove the Settings page only when the rulesRevamp beta is on. With the beta off, users still need it (those rows only move to Rules when the beta is on). So I plan to remove it (and redirect its routes to the list page) when the beta is on, and keep it as a fallback when the beta is off.

What do you think — does this work, or should I remove it fully now? Thanks!

@marufsharifi If we keep the Settings page as a fallback when the beta is off, which was the original design of the PR, then I think we should create a follow-up issue to fully remove the Settings page and the fallback code once rulesRevamp reaches 100% rollout. This will help make sure we don't forget to clean it up later. Right?

@huult

huult commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

@marufsharifi please resolve the conflict

@marufsharifi

Copy link
Copy Markdown
Contributor Author

@marufsharifi If we keep the Settings page as a fallback when the beta is off, which was the original design of the PR, then I think we should create a follow-up issue to fully remove the Settings page and the fallback code once rulesRevamp reaches 100% rollout. This will help make sure we don't forget to clean it up later. Right?

Yes, that's sound good to create a follow-up issue to fully remove the Settings page. thanks

@huult

huult commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

@marufsharifi #99886 (comment) Could you update this to Hold?

@marufsharifi marufsharifi changed the title Move Categories & Tags settings into the header More menu [HOLD for #98030] Move Categories & Tags settings into the header More menu Sep 10, 2026
@marufsharifi

Copy link
Copy Markdown
Contributor Author

@huult Put this on HOLD until #98030 re-lands. thanks

@huult

huult commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

@marufsharifi Please keep this PR on hold until PR #100248 is completed.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants