Skip to content

feat(account-selector): fire optional onSelectAccount route param on every tap#30318

Merged
Brunonascdev merged 5 commits into
mainfrom
feat/account-selector-on-select-callback
May 18, 2026
Merged

feat(account-selector): fire optional onSelectAccount route param on every tap#30318
Brunonascdev merged 5 commits into
mainfrom
feat/account-selector-on-select-callback

Conversation

@Brunonascdev

@Brunonascdev Brunonascdev commented May 18, 2026

Copy link
Copy Markdown
Contributor

Description

This branch wires up the existing onSelectAccount route param on AccountSelectorParams so callers opening the picker via createAccountSelectorNavDetails receive a callback on every tap inside the picker — including taps on the already-selected account.

Previous behaviour: The onSelectAccount route param was declared on AccountSelectorParams but never invoked anywhere. Consumers that needed to know "the user committed a selection" had to read Redux state diffs on AccountTreeController:selectedAccountGroupChange, which does not emit when the user re-taps the already-selected account. Combined with the fact that both tap-commit and back-out paths end in navigation.goBack(), callers had no signal to distinguish a same-account commit from a dismiss.

New behaviour:

  1. Tap any account (different or same)_onSelectMultichainAccount calls setSelectedAccountGroup (existing behavior), then invokes the route param onSelectAccount(accountGroup), then closes. The callback fires regardless of whether the controller actually emitted a state change.
  2. Back out / dismiss — Unchanged: navigation.goBack() only; callback does NOT fire.
  3. No callback provided — Unchanged: picker behaves identically to before.

Signature updated from (address: string) => void to (accountGroup: AccountGroupObject) => void to match the multichain context. No production caller passes this route param today (verified via repo-wide search for createAccountSelectorNavDetails), so this is a safe, additive surface change.

Why

  • Re-tapping the already-selected account inside a generic picker is a real user signal in flows that have a "virtual" funding source layered on top of the regular account (e.g. Money Account as Card funding source on the spending limit screen). Without this callback, consumer screens have no way to detect it.
  • Wiring the existing param keeps the public surface unchanged and avoids introducing a parallel naming convention.

What changed (scoped paths)

Area Files / behaviour
Picker route param type app/components/Views/AccountSelector/AccountSelector.types.ts — updated onSelectAccount signature to (accountGroup: AccountGroupObject) => void and clarified JSDoc that it fires on every tap.
Picker wiring app/components/Views/AccountSelector/AccountSelector.tsx — destructure onSelectAccount from route params and invoke inside _onSelectMultichainAccount between setSelectedAccountGroup and handleClose.
Tests app/components/Views/AccountSelector/AccountSelector.test.tsx — 3 new tests: callback fires with the tapped group; callback still fires when re-tapping the already-selected account; picker does not throw when the callback is absent. Updated existing mock's signature.

Out of scope (intentional)

  • Other selector components that have their own component-level onSelectAccount prop (MultichainAccountSelectorList, AccountListCell, AccountConnectSingleSelector, etc.) — none touched.
  • No changes to controllers, messengers, or default picker behavior for any existing consumer.

Changelog

CHANGELOG entry: Wired the existing onSelectAccount route param on the global Account Selector picker so callers receive a callback on every account tap (including re-tapping the already-selected account), enabling consumer screens to distinguish a committed selection from a dismiss.

Related issues

Fixes:

Used by companion PR #30320 (Card spending limit) to detect the "user is exiting Money Account mode by re-tapping the same regular account" case.

Manual testing steps

Feature: Optional onSelectAccount callback on the global Account Selector picker

  Background:
    Given a caller opens the Account Selector via createAccountSelectorNavDetails
    And the user has more than one account group

  Scenario: caller passes onSelectAccount and the user taps a different account
    When the user taps an account group different from the current one
    Then the picker calls setSelectedAccountGroup with the tapped group's id
    And the onSelectAccount callback is invoked with the tapped account group
    And the picker closes

  Scenario: caller passes onSelectAccount and the user re-taps the already-selected account
    When the user taps the account group that is already selected
    Then setSelectedAccountGroup is still called (no state change emitted)
    And the onSelectAccount callback is invoked with the tapped account group
    And the picker closes

  Scenario: caller passes onSelectAccount and the user dismisses without picking
    When the user taps the back arrow / swipes the picker down
    Then the onSelectAccount callback is NOT invoked
    And the picker closes

  Scenario: caller does NOT pass onSelectAccount (existing flows)
    Given the caller opens the picker without the onSelectAccount route param
    When the user taps any account
    Then the picker behaves identically to before (setSelectedAccountGroup + close)
    And no error is thrown

Screenshots/Recordings

Before

After

Pre-merge author checklist

Performance checks (if applicable)

  • I've tested on Android
    • Ideally on a mid-range device; emulator is acceptable
  • I've tested with a power user scenario
    • Use these power-user SRPs to import wallets with many accounts and tokens
  • I've instrumented key operations with Sentry traces for production performance metrics

For performance guidelines and tooling, see the Performance Guide.

Pre-merge reviewer checklist

  • I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed).
  • I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots.

Note

Medium Risk
Changes the Account Selector navigation contract by invoking an optional route callback on selection and altering its signature to pass an AccountGroupObject, which could impact any existing callers and selection flow ordering.

Overview
Account Selector now invokes an optional onSelectAccount route param on every account tap. AccountSelector wires route.params.onSelectAccount into _onSelectMultichainAccount, calling it after AccountTreeController.setSelectedAccountGroup and before closing the sheet, so callers get a commit signal even when re-tapping the already-selected account.

API and tests updated. AccountSelectorParams.onSelectAccount now receives an AccountGroupObject (instead of an address string) with updated JSDoc, and AccountSelector.test.tsx adds coverage for callback firing (same/different account), ordering vs goBack, non-callback behavior, dismiss via back arrow, reload flag reset, and sub-screen back navigation.

Reviewed by Cursor Bugbot for commit ad94198. Bugbot is set up for automated code reviews on this repo. Configure here.

@Brunonascdev Brunonascdev requested a review from a team as a code owner May 18, 2026 14:22
@github-actions

Copy link
Copy Markdown
Contributor

CLA Signature Action: All authors have signed the CLA. You may need to manually re-run the blocking PR check if it doesn't pass in a few minutes.

@metamaskbotv2 metamaskbotv2 Bot added the team-card Card Team label May 18, 2026
@Brunonascdev Brunonascdev self-assigned this May 18, 2026
@github-actions github-actions Bot added pr-not-ready-for-e2e Skip E2E and block merging. Remove this label once the PR is ready to run the E2E tests. size-M labels May 18, 2026

@cursor cursor Bot 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.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit ac85085. Configure here.

Comment thread app/components/Views/AccountSelector/AccountSelector.test.tsx
@Brunonascdev Brunonascdev enabled auto-merge May 18, 2026 18:03
@Brunonascdev Brunonascdev added skip-sonar-cloud Only used for bypassing sonar cloud when failures are not relevant to the changes. pr-not-ready-for-e2e Skip E2E and block merging. Remove this label once the PR is ready to run the E2E tests. and removed pr-not-ready-for-e2e Skip E2E and block merging. Remove this label once the PR is ready to run the E2E tests. skip-sonar-cloud Only used for bypassing sonar cloud when failures are not relevant to the changes. labels May 18, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🔍 Smart E2E Test Selection

  • Selected E2E tags: SmokeAccounts, SmokeWalletPlatform, SmokeConfirmations, SmokeNetworkExpansion, SmokeSnaps, SmokeMultiChainAPI, SmokeNetworkAbstractions
  • Selected Performance tags: @PerformanceAccountList
  • Risk Level: medium
  • AI Confidence: 82%
click to see 🤖 AI reasoning details

E2E Test Selection:
The changes modify the AccountSelector component's onSelectAccount callback in two significant ways:

  1. API signature change: onSelectAccount changed from (address: string) => void to (accountGroup: AccountGroupObject) => void — a breaking change for any callers using this route param callback.

  2. Behavioral change: The callback now fires on EVERY account tap (including re-tapping the already-selected account), and fires BEFORE handleClose(). This changes the semantics of the callback.

Why these tags:

  • SmokeAccounts: AccountSelector is the primary UI for account switching, SRP management, account creation, and account import flows. Any regression here directly impacts account management tests.
  • SmokeWalletPlatform: AccountSelector is used in wallet home flows, multi-SRP architecture, and account deletion flows. The EVM provider accountsChanged event is triggered by account selection.
  • SmokeConfirmations: Account selection is part of confirmation flows (e.g., per-dApp network selection, custom-amount-info uses AccountSelector). The confirmations AccountSelector component also uses similar patterns.
  • SmokeNetworkExpansion: Solana account switching and dApp notification flows use AccountSelector. The multi-chain provider system relies on account selection working correctly.
  • SmokeSnaps: SnapUIAccountSelector imports from AccountSelector. Snap account management and keyring snaps depend on account selection.
  • SmokeMultiChainAPI: CAIP-25 session management involves account selection across chains. AccountPermissionsConnected uses onSelectAccount for switching active accounts in dApp sessions.
  • SmokeNetworkAbstractions: Chain permission system and dApp chain access management involve account selection flows.

The change is scoped to the route-param callback (not the internal account list selection logic), so the core account switching Redux flow is unchanged. However, any callers that pass onSelectAccount as a route param and expect a string address will now receive an AccountGroupObject instead — this could silently break callers. The extensive new unit tests suggest the author is confident in the implementation, but E2E validation is warranted given the broad usage of AccountSelector.

Performance Test Selection:
The AccountSelector component is directly measured by @PerformanceAccountList which covers account selector rendering and dismissal performance. The behavioral change (callback firing on every tap including re-taps, and firing before handleClose) could affect the timing of account list dismissal and rendering cycles. This warrants a performance test run to ensure no regression in account list interaction responsiveness.

View GitHub Actions results

@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
75.0% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube Cloud

@Brunonascdev Brunonascdev added this pull request to the merge queue May 18, 2026
Merged via the queue into main with commit edc22ad May 18, 2026
289 of 298 checks passed
@Brunonascdev Brunonascdev deleted the feat/account-selector-on-select-callback branch May 18, 2026 19:39
@github-actions github-actions Bot locked and limited conversation to collaborators May 18, 2026
@metamaskbotv2 metamaskbotv2 Bot added the release-7.79.0 Issue or pull request that will be included in release 7.79.0 label May 18, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

release-7.79.0 Issue or pull request that will be included in release 7.79.0 size-M skip-sonar-cloud Only used for bypassing sonar cloud when failures are not relevant to the changes. team-card Card Team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants