Skip to content

Remove legacy Button component, clean seatbelt - #100000

Merged
mountiny merged 27 commits into
Expensify:mainfrom
software-mansion-labs:Guccio163/button/cleanup
Sep 10, 2026
Merged

mountiny merged 27 commits into
Expensify:mainfrom
software-mansion-labs:Guccio163/button/cleanup

Conversation

@Guccio163

@Guccio163 Guccio163 commented Sep 1, 2026 •

Copy link
Copy Markdown
Contributor

This PR finishes the legacy Button to composed Button migration.

  • Removes the legacy Button component — deletes src/components/Button/index.tsx (562 lines), its Storybook story, and its unit test, along with stale jest.mock('@components/Button') mocks in tests that no longer import it. The composed version takes over its name and role.
  • Renames ButtonComposed -> Button — the final name once the old component is gone. Updates imports across ~370+ files in src and tests.
  • Cleans up internals of the Button component: replaces unsafe type casts (currentTarget as HTMLElement, ref as PressableRef) with instanceof narrowing; colocates validateSubmitShortcut with its sole consumer (ButtonKeyboardShortcut); removes the dead getButtonRole() helper and inlines CONST.ROLE.BUTTON at its 15 call sites.
  • Consolidates ButtonDisabledWhenOffline — moves it into the new Button folder, drops a redundant prop, and adopts it at several call sites that previously implemented the disabled+offline pattern manually.
  • Post-migration hygiene: removes dead type exports that were blocking knip, fixes leftover ButtonComposed mentions in comments/test descriptions, trims eslint.seatbelt.tsv entries tied to removed/fixed code, resyncs unrelated stale compiled GitHub Actions bundles to cut diff noise, and renames/updates tests to match the new structure.

Explanation of Change

Fixed Issues

$ #95180
PROPOSAL:

Tests

Most of this PR's changes are NoQA; beside cleanup, the ButtonDisabledWhenOffline was implemented in place of Button's sharing the isDisabled={isOffline} behaviour; Verify offline-disabled buttons (e.g. Workspace Member Details, Add Delegate, Lock Account) show as disabled while offline.

  1. Click your account avatar in the bottom-left of the navigation bar to open Settings.
  2. Click Security.
  3. Click Report suspicious activity.
  4. Confirm the Report suspicious activity button at the bottom of the page is visible and enabled.
  5. Enforce offline mode, f.ex. in browser developer panel or in Settings -> Troubleshoot -> enable Force offline.
  6. Return to the Lock Account page (Settings > Security > Report suspicious activity) and confirm the button is now disabled.
  • Verify that no errors appear in the JS console

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 any callback methods that were added or modified are named for what the method does and never what callback they handle (i.e. toggleReport and not onIconClick)
    • 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 shown in the product is localized by adding it to src/languages/* files and using the translation method
      • If any non-english text was added/modified, I used JaimeGPT to get English > Spanish translation. I then posted it in #expensify-open-source and it was approved by an internal Expensify engineer. Link to Slack message:
    • I verified all numbers, amounts, dates and phone numbers shown in the product are using the localization methods
    • 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)
    • I verified proper file naming conventions were followed for any new files or renamed files. All non-platform specific files are named after what they export and are not named "index.js". All platform-specific files are named for the platform the code supports as outlined in the README.
    • I verified the JSDocs style guidelines (in STYLE.md) were followed
  • 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)
  • I verified all code is DRY (the PR doesn't include any logic written more than once, with the exception of tests)
  • I verified any variables that can be defined as constants (ie. in CONST.ts or at the top of the file that uses the constant) are defined as such
  • I verified that if a function's arguments changed that all usages have also been updated correctly
  • If any new file was added I verified that:
    • The file has a description of what it does and/or why is needed at the top of the file if the code is not self explanatory
  • 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.
  • If a new page is added, I verified it's using the ScrollView component to make it scrollable when more elements are added to the page.
  • 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
Screen.Recording.2026-09-03.at.16.22.08.mov

…omplete

Deletes the old src/components/Button/index.tsx, its story, and its unit
test, plus stale jest.mock('@components/Button') mocks in tests whose
components under test no longer import it.
@Guccio163 Guccio163 changed the title Remove legacy Button component now that ButtonComposed migration is c… Remove legacy Button component, clean seatbelt Sep 1, 2026
@JakubKorytko

Copy link
Copy Markdown
Member

@GCyganek

GCyganek commented Sep 1, 2026

Copy link
Copy Markdown
Contributor
image

@ZhenjaHorbach

Copy link
Copy Markdown
Contributor

Don't forget to remove the ESLint rule too
Since we won't have @components/Button anymore 😁

@codecov

codecov Bot commented Sep 2, 2026 •

Copy link
Copy Markdown

isDeployChecklistLocked and proposalPoliceComment index.js had drifted from main's regenerated bundle output; resync removes unrelated noise from this branch's diff.
getButtonRole always returned the same constant regardless of its isNested arg on both platform variants, so the indirection was dead weight. Inlined the constant at all 15 call sites and removed the now-unused helper and its type.
…onKeyboardShortcut

It was only used by ButtonKeyboardShortcut, so move it out of the legacy Button/ folder into a ButtonKeyboardShortcut/ subfolder under ButtonComposed/primitives, alongside the component it serves. Also swap the unsafe HTMLInputElement cast for instanceof narrowing, matching the pattern already used elsewhere (e.g. NavigationFocusReturn), so the moved file keeps a clean eslint-seatbelt baseline instead of inheriting a stale grandfathered violation tied to the old path.
…utton

Matches the instanceof pattern already used elsewhere in the codebase (e.g. NavigationFocusReturn) instead of asserting to HTMLElement blindly.
Mocks, imports, and describe/comment text still pointed at the old @components/ButtonComposed path, breaking module resolution now that the composed Button was renamed to Button.
Stale docstrings and a comment still named the old ButtonComposed path; updated them to Button. Also dropped the orphaned eslint-seatbelt row for the deleted ButtonComposed/Button.tsx (local runs are read-only by default so the tool never pruned it itself).
LinkButtonProps, ButtonIconProps, ButtonEventsProps, ButtonBehaviorProps, ButtonStyleProps, and BaseButtonProps were exported but never imported anywhere outside their own file. The ButtonComposed -> Button rename moved this pre-existing knip finding to a new path, which the CI knip-compare check treats as a new violation regardless of the matching resolved one. Dropping the export (the types are still used internally) removes the finding outright instead of just relocating it.
…ibe label

Old name/label were leftovers from the ButtonComposed -> Button rename; the collision with the legacy Button test no longer exists since that file was removed.
@melvin-bot

melvin-bot Bot commented Sep 3, 2026

Copy link
Copy Markdown

Hey! I see that you made changes to our Form component. Make sure to update the docs in FORMS.md accordingly. Cheers!

@Guccio163
Guccio163 marked this pull request as ready for review September 3, 2026 14:24
@Guccio163
Guccio163 requested review from a team as code owners September 3, 2026 14:24
@melvin-bot
melvin-bot Bot requested review from ZhenjaHorbach and flaviadefaria and removed request for a team September 3, 2026 14:24
@Guccio163

Copy link
Copy Markdown
Contributor Author

@ZhenjaHorbach @mountiny ready ✅

@ZhenjaHorbach

Copy link
Copy Markdown
Contributor

Let's merge this!

@ZhenjaHorbach

Copy link
Copy Markdown
Contributor

@Guccio163
Let's fix conflicts!

@Guccio163

Guccio163 commented Sep 9, 2026 •

Copy link
Copy Markdown
Contributor Author

Conflicts fixed, waiting for checks to pass

@ZhenjaHorbach

ZhenjaHorbach commented Sep 9, 2026 •

Copy link
Copy Markdown
Contributor

@Guccio163
Let's try again
Because we merged this PR just now and have conflits 😅

@Guccio163

Copy link
Copy Markdown
Contributor Author

I hope we merge this one soon then 😆

@ZhenjaHorbach

Copy link
Copy Markdown
Contributor

@mountiny
Let's merge this!

@mountiny mountiny 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.

Thanks for your patience, sorry for the delay

Image

@mountiny
mountiny merged commit cfa2aaf into Expensify:main Sep 10, 2026
34 of 35 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

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

@ZhenjaHorbach

Copy link
Copy Markdown
Contributor
squid-game-squid-game-3

@OSBotify

Copy link
Copy Markdown
Contributor

✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release.

@OSBotify

Copy link
Copy Markdown
Contributor

🚀 Deployed to staging by https://github.com/mountiny in version: 9.4.76-0 🚀

platform result
🕸 web 🕸 success ✅
🤖 android 🤖 success ✅
🍎 iOS 🍎 success ✅

@MelvinBot

MelvinBot commented Sep 10, 2026 •

Copy link
Copy Markdown
Contributor

🤖 No help site changes are required for this PR. I did not create a docs PR.

Why: This is an internal component refactor with no user-visible change. Across all 398 changed files, nothing touches docs/ or src/languages/, so no help-site content and no product copy changed.

What I checked

Change surface: 398 files, all under src/, tests/, and two eslint config files. Zero files under docs/articles and zero under src/languages/, so no user-facing strings were added, removed, or reworded.

The three changes that could plausibly have been user-facing, and why they aren't:

  1. Legacy Button removed, ButtonComposed renamed to Button — a rename of an internal component and its ~370 import sites. No rendered output changes.

  2. ButtonDisabledWhenOffline consolidated — at src/pages/settings/Security/LockAccount/LockAccountPageBase.tsx and src/pages/settings/Security/AddDelegate/ConfirmDelegatePage.tsx, the explicit isDisabled={isOffline} prop was replaced by the ButtonDisabledWhenOffline wrapper. Same offline-disabled behavior, expressed once instead of at each call site. Nothing new is disabled offline that wasn't before.

  3. Internal cleanup — type-cast narrowing, removing the dead getButtonRole() helper, trimming eslint.seatbelt.tsv, and resyncing compiled GitHub Actions bundles. All build- and lint-time only.

Button labels are unchanged. Every label in the touched pages still resolves through the same translate() keys (for example lockAccountPage.reportSuspiciousActivity, delegate.addCopilot), and those keys were not edited.

Help articles covering these flows — account security and Copilot setup — describe what the buttons do, not how the component is built, so they stay accurate as written.

Since no help site changes are needed, there is no docs PR to review, and nothing to label HelpDot or assign. If you expected a user-facing change from this PR that I missed, reply with @MelvinBot <what changed> and I'll draft the docs update.

@Guccio163, please confirm you agree that no help site update is needed here. If you'd rather I create the docs PR anyway, say so and I'll open it as a draft assigned to you.


view run · no recording available

@OSBotify

Copy link
Copy Markdown
Contributor

🚀 Deployed to production by https://github.com/mountiny in version: 9.4.76-1 🚀

platform result
🕸 web 🕸 success ✅
🤖 android 🤖 success ✅
🍎 iOS 🍎 success ✅

Bundle Size Analysis (Sentry):

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.

8 participants