Skip to content

Migrate Button to ButtonComposed (batch 7)#96731

Draft
mikolajpochec wants to merge 3 commits into
Expensify:mainfrom
software-mansion-labs:@mikolajpochec/button-migration-pr7
Draft

Migrate Button to ButtonComposed (batch 7)#96731
mikolajpochec wants to merge 3 commits into
Expensify:mainfrom
software-mansion-labs:@mikolajpochec/button-migration-pr7

Conversation

@mikolajpochec

@mikolajpochec mikolajpochec commented Jul 22, 2026

Copy link
Copy Markdown
Member

Explanation of Change

This is batch 7 (PR7 of 9) of the effort to migrate every direct <Button> usage (import Button from '@components/Button') over to the composed ButtonComposed, so the old Button can be deprecated. This batch is the "text + icon · styled" shape. 38 files / 59 instances were listed for this batch; 2 needed no change (MoneyReportHeaderSecondaryActions.tsx was already migrated via #93789; SpendOverTimeSectionContent.tsx no longer has a <Button>), so this PR touches 36 files.

Fixed Issues

$ #95176
PROPOSAL: #83762 (comment)

Tests

For every migrated button, verify that behaviour is unchanged from before the migration:

  1. Label renders correctly and is not clipped or truncated.
  2. Variant/color is correct — success = green, danger = red, default = grey.
  3. Size is correct — SMALL vs LARGE height/padding matches the previous look.
  4. Disabled state — the button greys out and is non-interactive when its precondition isn't met (dirty check, required field, offline).
  5. Loading state — the spinner replaces the label and the button is non-interactive while a request is in flight (isLoading).
  6. Press action — tapping fires the same documented action as before (navigates / submits / opens modal / external link).
  7. Enter-key shortcut (where applicable) — pressing Enter on web fires the same action (<Button.KeyboardShortcut>).

Moreover, I divided migrated buttons into several categories, based on their custom styling or behavior which is applied in the application:

  • Plain left-icon + text button, no conditional state
  • Full-width CTA with a right-edge icon — icon+text grouped on the left, right icon pinned to the far edge (the shape that needed the SetupMethod layout fix)
  • Right-side icon on a content-width button — a trailing icon on a pill that hugs its content
  • LinkButton — link-styled text + icon with a hover color (desktop only)
  • Content-container constraint + conditional varianticonWrapperStylescontentContainerStyle, plus success mapped to a conditional variant

For each of these categories, detailed test steps are described in sections below:

Plain left-icon + text button
Preconditions
  1. Be an Admin of a Control/Collect workspace (e.g. from a "Manage my team's expenses" account). Categories are on by default for such a workspace.
Test steps
  1. Navigate to Workspaces > Workspace > Categories.
  2. Look at the green Add category button at the top-right of the list.
  3. Press it.
Expected behavior

The Add category button renders the Plus icon immediately left of the label (grouped, not split), is a green success button, and matches the rendering from a production build; pressing it opens the create-category page.


Full-width CTA with a right-edge icon
Preconditions
  1. A validated account with no bank account / payment method yet, whose wallet is not already activated.
Test steps
  1. Deep link to /settings/wallet/enable-payments/add-bank-account, or navigate Account menu (avatar) > Wallet > Enable wallet.
  2. Look at the green Add bank account button.
Expected behavior

The left Bank icon + "Add bank account" text are grouped on the left, and the ArrowRight icon is pinned to the far-right edge.

Note: this button renders slightly differently from the production build (left/right padding), and that's expected/acceptable.


Right-side icon on a content-width button
Preconditions

None beyond a normal account.

Test steps
  1. Start a Scan expense: FAB (+) > Create expense > Scan tab > scan/upload a receipt.
  2. Pick a participant/workspace and proceed to the Confirm details screen.
  3. Look for the Show more button in the footer.
Expected behavior

Show more renders with the down-arrow directly to its right, centered on the divider line, matching production.


LinkButton — "Columns" (desktop web only)
Preconditions
  1. A workspace and an expense report on it with at least 2 expenses (a personal/single-expense/empty report shows a different screen with no Columns link).

⚠️ This link is desktop web only — hidden on mobile web, native iOS, and native Android.

Test steps
  1. Create two expenses on the workspace (Create expense → amount → choose the workspace → submit, twice, so both land on the same report).
  2. Open that report from Reports (or the workspace chat in the Inbox) — you'll see a table of expenses.
  3. At the top-right of the table, hover the blue Columns link (watch the color), then click it.
Expected behavior

At rest the "Columns" text is link-blue and its icon is the same blue; on hover both shift to the hover-blue; clicking opens the column-selection page. Matches production.


Content-container constraint + conditional variant (free-trial pill)
Preconditions
  1. A fresh "Manage my team's expenses" onboarding account (this creates a paid workspace and starts a free trial, which is what makes the pill appear).
Test steps
  1. Open the Inbox and open the workspace #admins room (the onboarding chat).
  2. Look at the green "★ Trial: N days left!" pill in the header (inline on desktop; a padded row below the header on mobile).
Expected behavior

The pill renders with the Star icon + trial text, the content stays constrained to the pill. Matches production in both wide and narrow placement.

Offline tests

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 button_left_icon_android button_right_icon_small_show_more_android button_trial_android button_two_icons_android
Android: mWeb Chrome button_left_icon_android_web button_right_icon_small_show_more_android_web button_trial_android_web button_two_icons_android_web
iOS: Native button_left_icon_ios button_right_icon_small_show_more_ios button_trial_ios button_two_icons_ios
iOS: mWeb Safari button_left_icon_ios_web button_right_icon_small_show_more_ios_web button_trial_ios_web button_two_icons_ios_web
MacOS: Chrome / Safari button_left_icon_web_large button_left_icon_web_small button_right_icon_small_show_more_web_small button_trial_web_large button_trial_web_small button_two_icons_web_large button_two_icons_web_small link_button_web

@Guccio163 Guccio163 left a comment

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.

Good work, leaving 2 nitpicks to include in the description; One more thing: Note: this button renders slightly differently from the production build (left/right padding), and that's expected/acceptable. in one of the test-step suites is a very good idea but should be defined clearly, describing exactly where are the mismatches and how big, f.ex. 'Production has 8px gap and should be 4px'. Beside that, LGTM 👍

<Button.Text>{translate('bankAccount.addBankAccount')}</Button.Text>
<Button.Icon
src={icons.ArrowRight}
style={styles.mlAuto}

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.

I'd be worth noting in the PR that we added this style as it is required to style the button correctly and it's an exception, just a comment in the PR description

Comment thread src/styles/index.ts
@@ -946,7 +946,7 @@ const staticStyles = (theme: ThemeColors) =>
},

buttonCTAIcon: {
marginRight: 22,
marginRight: 18,

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.

Leave a note in the PR description that you're changing that because this is a custom style used only in 1 place and we are decreasing it by 1 to account for 4px constant gap , that sums up with this 18px to previous value of 22px.

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