Skip to content

[No QA] Add a collapsing flat navigation bar - #102023

Draft
shawnborton wants to merge 56 commits into
mainfrom
shawn-flatNavCollapse
Draft

shawnborton wants to merge 56 commits into
mainfrom
shawn-flatNavCollapse

Conversation

@shawnborton

Copy link
Copy Markdown
Contributor

Explanation of Change

This builds on the flat navigation bar and adds a collapsing left bar on wide layouts. It is opened as a separate pull request so that the other one keeps its own test build to compare against.

This replaces the 72px skinny left bar on web and desktop with a 220px flat navigation bar, and reworks the narrow layout's bottom bar to match.

Wide layout

Every destination is a full-width row with a horizontal icon and label. The Spend pages no longer render a second left nav — their searches move into the main nav as child rows, so there is one navigation surface instead of two.

The nav lists Home, Inbox, Expenses, Reports, Accounting, Saved, Insights (behind the insightsPage beta) and Workspaces, then an account row with your avatar and name. Reports, Accounting and Saved reveal their searches as child rows only while that group is the active destination. A collapsed group carries its children's combined count as a badge; expanding it drops that badge so the children's own counts do not double-report the same work.

At the top sit the Expensify wordmark and a small green create button that opens the global create menu.

Narrow layout

The bottom bar is Home, Inbox, Expenses, Reports and More. The Spend tab row shows only the group you are in — Reports shows its four searches, Accounting its five — and hides entirely for Expenses, which is a group of one. The page title names the group, so it holds still while you tab within it.

More opens a page of navigation rows: Accounting, Saved, Insights and Workspaces. Each row returns you to where you last were inside that destination rather than to its first page, and each of those pages gets a back arrow to the More list. Tapping More while you are inside one of its destinations climbs back to the list.

Collapsing the bar

A toggle in the bar's header narrows it from 220px to a 60px icon rail, and the central pane follows. Every row keeps its normal padding while collapsed, so the icons stay on the same column and none of them move as the bar animates; the labels, badges, group children and hover menus simply go away.

Hovering a collapsed bar peeks it back open over the content rather than pushing the page around, and moving the pointer away closes it again.

The collapsed state reuses the store the Spend sidebar already had, SearchSidebarCollapseStore. That sidebar is the surface this navigation replaced, so sharing one persisted flag and one peek state avoids two that can disagree. The flat navigation bar adds its own width hooks alongside the existing ones.

Three places assumed the bar was a fixed 220px and now read its live width: the navigator's own container, which is what slides the central pane; DebugTabView's absolute positioning; and SearchRowSkeleton, which right-aligns against the left pane.

Shared changes

  • Selected and hover backgrounds are shared across navigation surfaces as navigationRowSelected (product-300) and navigationRowHovered (product-200). They apply to the flat nav, the Inbox LHN, and the Workspace, Domain and Account editor menus. The editor menus opt in through a new shouldUseNavigationRowColors prop on MenuItem, so popover and context menus that also use focused keep their current colors.
  • Horizontal tabs use product-300 for the selected tab, and can opt into hugging their content instead of stretching.
  • Sidebar widths are per-navigator: 360px for the Inbox, 260px for Spend, Workspace, Domain and Account. getSplitNavigatorSidebarWidth resolves the width from the navigator's sidebar route so the sidebar and the central pane offset always agree.
  • On web the Copilot Switch button moves into the Account page header, which leaves the full sidebar width for the user name.
  • Wide-layout navigation rows are 40px tall and share one horizontal padding constant, so their labels line up.

Two fixes that reach beyond the new navigation

  1. WORKSPACES_TAB_SET did not list SCREENS.DOMAINS_LIST, so the Workspaces tab could not restore onto its Domains tab and always fell back to Workspaces. This is reproducible on main today: open Workspaces, switch to Domains, leave the tab, and come back.
  2. SearchRowSkeleton positioned its trailing elements against a left pane that included the Spend sidebar this PR removes, leaving a gap on the right of every skeleton row.

Fixed Issues

$
PROPOSAL:

Tests

Wide layout

  1. Open the app on web in a window at least 1400px wide.
  2. Verify the left nav is 200px wide and lists Home, Inbox, Expenses, Reports, Accounting, Workspaces and an account row.
  3. Click Reports. Verify it opens All reports and reveals All reports, Drafts, Needs approval and Ready to pay beneath it.
  4. Verify the child rows have no icons, use the smaller label size, and that only the active child is bold.
  5. Click Accounting. Verify the Reports children collapse and the Accounting children appear.
  6. Navigate away from a group. Verify its row shows a single badge totalling its children's counts, and that the badge disappears once the group is expanded.
  7. Save a search. Verify a Saved row appears and lists that search as a child.
  8. Hover any top-level row. Verify the label turns the standard text color and the icon turns green.
  9. Verify the account row shows your avatar and name, and that selecting it draws a green ring around the avatar without changing the row height.
  10. Open a Spend page. Verify there is no second left nav beside the main one.
  11. Open Settings, a workspace, and a domain. Verify each sidebar is 260px and the Inbox sidebar is 360px, with no gap or overlap against the central pane.

Narrow layout

  1. Narrow the window below the wide-layout breakpoint, or open the app on a phone.
  2. Verify the bottom bar is Home, Inbox, Expenses, Reports and More.
  3. Open Expenses. Verify there is no tab row above the table and no empty gap where it would have been.
  4. Open Reports. Verify the tab row shows only All reports, Drafts, Needs approval and Ready to pay, and that the page title stays "Reports" as you switch tabs.
  5. Tap More. Verify a page titled "More" listing Accounting, Saved, Insights and Workspaces.
  6. Tap Accounting, switch to Card statements, then tap Home and tap More again. Verify you land back on Card statements, not on the More list.
  7. Tap More again while on Card statements. Verify you return to the More list.
  8. Verify Accounting, Saved, Insights and Workspaces each show a back arrow that returns to the More list.
  9. From More, open Workspaces, switch to the Domains tab, go to Home, then tap More. Verify you return to the Domains tab.
  10. Verify the account avatar appears in the top right and opens Account.

Collapsing the bar

  1. On a wide window, click the toggle at the top right of the left navigation bar. Verify the bar narrows to an icon rail and the page content moves left with it.
  2. Verify each row's icon stays on the same column through the animation, rather than sliding to a new position.
  3. Verify labels, badges, group children and the saved search overflow menus are gone while collapsed, and the create button is a green square.
  4. Hover the collapsed bar. Verify it widens back out over the content without moving the page, and closes again when you move away.
  5. Click the toggle again. Verify the bar returns to 220px and every label comes back.
  6. Reload the page. Verify the bar is still collapsed.
  7. Collapse the bar and open a Spend page while its results are loading. Verify the loading skeleton's trailing elements stay against the right edge.

Failure and edge scenarios

  1. Sign in with an account that has no saved searches. Verify no Saved row appears in the wide nav or on the More page.
  2. Sign in without the insightsPage beta. Verify no Insights row appears in either place, and that Account is still reachable from the top right.
  3. Delete the saved search you last opened from More, then tap More. Verify it falls back to the More page instead of failing.
  4. Reload the page and tap More. Verify it opens the More list, since the remembered destination is session-scoped.
  • Verify that no errors appear in the JS console

Offline tests

  1. Load the app online, then turn off your network connection.
  2. Verify the left nav still renders every row, including group children and the account row.
  3. Click between Reports, Accounting and Workspaces. Verify navigation still works and the selected row updates.
  4. On a narrow window, tap More and open each destination. Verify navigation works and the back arrows return to the list.
  5. Verify the offline indicator appears and the nav does not shift or collapse.

QA Steps

Same as tests.

  • 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
Android: mWeb Chrome
iOS: Native
iOS: mWeb Safari
MacOS: Chrome / Safari

🤖 Generated with Claude Code

shawnborton and others added 18 commits September 15, 2026 15:52
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… to the top bar

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
# Conflicts:
#	src/components/AccountSwitcher.tsx
#	src/pages/Search/SearchPageWide.tsx
#	src/pages/inbox/sidebar/InboxTabSelector.tsx
#	src/pages/inbox/sidebar/ProfileAvatarWithIndicator.tsx
…e Spend tabs

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ation

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…n rows

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
# Conflicts:
#	src/styles/variables.ts
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…youts

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…dius

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

🚧 shawnborton has triggered a test Expensify/App build. You can view the workflow run here.

@github-actions

This comment has been minimized.

@github-actions

Copy link
Copy Markdown
Contributor

🚧 shawnborton has triggered a test Expensify/App build. You can view the workflow run here.

@github-actions

This comment has been minimized.

@github-actions

Copy link
Copy Markdown
Contributor

🚧 shawnborton has triggered a test Expensify/App build. You can view the workflow run here.

@github-actions

This comment has been minimized.

@github-actions

Copy link
Copy Markdown
Contributor

🚧 shawnborton has triggered a test Expensify/App build. You can view the workflow run here.

@github-actions

github-actions Bot commented Oct 5, 2026

Copy link
Copy Markdown
Contributor

🚧 shawnborton has triggered a test Expensify/App build. You can view the workflow run here.

@github-actions

This comment has been minimized.

@github-actions

github-actions Bot commented Oct 5, 2026

Copy link
Copy Markdown
Contributor

🚧 shawnborton has triggered a test Expensify/App build. You can view the workflow run here.

@github-actions

This comment has been minimized.

@github-actions

github-actions Bot commented Oct 5, 2026

Copy link
Copy Markdown
Contributor

🚧 shawnborton has triggered a test Expensify/App build. You can view the workflow run here.

@github-actions

This comment has been minimized.

@github-actions

github-actions Bot commented Oct 5, 2026

Copy link
Copy Markdown
Contributor

🚧 shawnborton has triggered a test Expensify/App build. You can view the workflow run here.

@github-actions

This comment has been minimized.

@github-actions

github-actions Bot commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

🚧 shawnborton has triggered a test Expensify/App build. You can view the workflow run here.

@github-actions

This comment has been minimized.

@OSBotify

This comment has been minimized.

@github-actions

github-actions Bot commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

🚧 shawnborton has triggered a test Expensify/App build. You can view the workflow run here.

@github-actions

This comment has been minimized.

@OSBotify

This comment has been minimized.

@OSBotify

Copy link
Copy Markdown
Contributor

🦜 Polyglot Parrot! 🦜

Squawk! Looks like you added some shiny new English strings. Allow me to parrot them back to you in other tongues:

The diff is too large to include in this comment (65KB), so I've created a gist for you:

📋 View the translation diff here 📋

Note

You can apply these changes to your branch by copying the patch to your clipboard, then running pbpaste | git apply, or directly by running gh gist view --raw 491d0f59a1f3361f7e7aaec639e52031 | git apply 😉

View workflow run

@github-actions

Copy link
Copy Markdown
Contributor

🚧 shawnborton has triggered a test Expensify/App build. You can view the workflow run here.

@github-actions

Copy link
Copy Markdown
Contributor

🧪🧪 Use the links below to test this adhoc build on Android, iOS, and Web. Happy testing! 🧪🧪
Built from App PR #102023.

Android 🤖 iOS 🍎
❌ FAILED ❌ ❌ FAILED ❌
The QR code can't be generated, because the Android build failed The QR code can't be generated, because the iOS build failed
Web 🕸️
https://102023.pr-testing.expensify.com
Web

👀 View the workflow run that generated this build 👀

This branch has not been deployed

No deployments
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