Skip to content

fix: respect source amount cursor position#27367

Merged
bfullam merged 21 commits into
mainfrom
swaps-input-cursor
Mar 20, 2026
Merged

fix: respect source amount cursor position#27367
bfullam merged 21 commits into
mainfrom
swaps-input-cursor

Conversation

@bfullam

@bfullam bfullam commented Mar 11, 2026

Copy link
Copy Markdown
Contributor

Description

This PR fixes Bridge source amount editing so keypad insertion and deletion respect the user-selected cursor position, including when the displayed amount contains locale formatting such as grouping separators.

The change keeps shared keypad components untouched and instead adds Bridge-local cursor handling:

  • adds useSourceAmountCursor to own source cursor state and keypad edit behavior
  • adds raw/formatted cursor mapping utilities so the displayed caret stays aligned with the raw editable amount
  • adds applyKeyAtCursor so insert/delete/decimal edits happen deterministically at the raw cursor position
  • updates TokenInputArea to support source selection and onSelectionChange
  • updates gasless quick-pick presets to replace the full amount explicitly instead of overloading keypad edit semantics

Changelog

CHANGELOG entry: Fixed a bug where Bridge amount edits did not respect the cursor position when using the keypad.

Related issues

Fixes: SWAPS-4131

Manual testing steps

Feature: Bridge source amount cursor-aware editing

  Scenario: insert a digit in the middle of a formatted amount
    Given the user opens Bridge
    And the source token is set
    And the user enters 1234 in the source amount
    And the displayed amount shows as 1,234
    When the user places the cursor between 2 and 3
    And the user taps 9 on the keypad
    Then the source amount updates to 12,934

  Scenario: delete a digit in the middle of a formatted amount
    Given the user opens Bridge
    And the user enters 1234 in the source amount
    And the displayed amount shows as 1,234
    When the user places the cursor after 2
    And the user taps backspace on the keypad
    Then the source amount updates to 134

  Scenario: select a quick-pick preset after editing the amount
    Given the user opens Bridge on a gas-sponsored quote path
    And quick-pick preset buttons are visible
    When the user taps a preset percentage button
    Then the source amount updates to the preset amount

  Scenario: apply Max after moving the cursor
    Given the user opens Bridge
    And the user moves the cursor in the source amount field
    When the user taps Max
    Then the source amount updates to the available max balance
    And subsequent keypad edits continue to work correctly

Screenshots/Recordings

Before

N/A

After

Screen.Recording.2026-03-18.at.12.02.49.mov

Pre-merge author checklist

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 how Bridge source amounts are edited and selected, including cursor mapping between formatted display values and raw input, which could impact entered swap amounts. Covered by new unit tests but touches core user input behavior.

Overview
Fixes Bridge source-amount editing so keypad insert/delete operations apply at the user-selected caret position even when the displayed amount includes locale formatting (grouping/decimal separators).

Adds useSourceAmountCursor plus new cursor-mapping and edit utilities (cursorPosition, applyKeyAtCursor) and wires controlled selection/onSelectionChange through TokenInputArea. Also updates Max/preset flows to normalize amounts via normalizeSourceAmountToMaxLength and changes gasless quick-pick presets to explicitly replace the amount (new onAmountSelect + tokenBalance prop), with updated/added tests.

Written by Cursor Bugbot for commit 166c4cd. This will update automatically on new commits. Configure here.

@bfullam bfullam requested a review from a team as a code owner March 11, 2026 16:19
@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.

Comment thread app/components/UI/Bridge/utils/applyKeyAtCursor.ts Outdated
@bfullam bfullam changed the title Add controlled cursor handling for bridge source input feat: add controlled cursor handling for bridge source input Mar 11, 2026
@bfullam bfullam added the team-swaps-and-bridge Swaps and Bridge team label Mar 11, 2026
Comment thread app/components/UI/Bridge/utils/applyKeyAtCursor.ts
…to swaps-input-cursor

# Conflicts:
#	app/components/UI/Bridge/Views/BridgeView/__snapshots__/BridgeView.test.tsx.snap
#	app/components/UI/Bridge/Views/BridgeView/index.tsx
Comment thread app/components/UI/Bridge/utils/cursorPosition.ts Outdated
Comment thread app/components/UI/Bridge/utils/applyKeyAtCursor.ts
Comment thread app/components/UI/Bridge/hooks/useSourceAmountCursor.ts
Comment thread app/components/UI/Bridge/Views/BridgeView/BridgeView.test.tsx
Comment thread app/components/UI/Bridge/Views/BridgeView/index.tsx
@bfullam bfullam changed the title feat: add controlled cursor handling for bridge source input fix(bridge): respect source amount cursor position Mar 17, 2026
@bfullam bfullam changed the title fix(bridge): respect source amount cursor position fix: respect source amount cursor position Mar 17, 2026
@github-actions github-actions Bot added the risk-medium Moderate testing recommended · Possible bug introduction risk label Mar 17, 2026
Comment thread app/components/UI/Bridge/hooks/useSourceAmountCursor.ts Outdated
Comment thread app/components/UI/Bridge/hooks/useSourceAmountCursor.ts Outdated
Comment thread app/components/UI/Bridge/utils/applyKeyAtCursor.ts Outdated
Comment thread app/components/UI/Bridge/utils/clamp.ts Outdated
@github-actions github-actions Bot added risk-medium Moderate testing recommended · Possible bug introduction risk and removed risk-medium Moderate testing recommended · Possible bug introduction risk labels Mar 17, 2026
@github-actions github-actions Bot added risk-medium Moderate testing recommended · Possible bug introduction risk and removed risk-medium Moderate testing recommended · Possible bug introduction risk labels Mar 18, 2026
infiniteflower
infiniteflower previously approved these changes Mar 18, 2026
infiniteflower
infiniteflower previously approved these changes Mar 18, 2026
@github-actions github-actions Bot added risk-medium Moderate testing recommended · Possible bug introduction risk and removed risk-medium Moderate testing recommended · Possible bug introduction risk labels Mar 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.

Comment thread app/components/UI/Bridge/utils/normalizeSourceAmountToMaxLength.ts Outdated
@github-actions github-actions Bot added risk-medium Moderate testing recommended · Possible bug introduction risk and removed risk-medium Moderate testing recommended · Possible bug introduction risk labels Mar 18, 2026
@github-actions github-actions Bot added risk-medium Moderate testing recommended · Possible bug introduction risk and removed risk-medium Moderate testing recommended · Possible bug introduction risk labels Mar 19, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🔍 Smart E2E Test Selection

  • Selected E2E tags: SmokeTrade, SmokeConfirmations
  • Selected Performance tags: None (no tests recommended)
  • Risk Level: medium
  • AI Confidence: 92%
click to see 🤖 AI reasoning details

E2E Test Selection:
All 14 changed files are within the Bridge UI feature (app/components/UI/Bridge/). The changes introduce:

  1. New cursor position utilities (applyKeyAtCursor.ts, cursorPosition.ts) - Handle mid-string editing in the source amount input
  2. New amount normalization utility (normalizeSourceAmountToMaxLength.ts) - Replaces trimTrailingZeros for max-length enforcement
  3. New hook (useSourceAmountCursor.ts) - Manages cursor state for the source amount input field
  4. Modified BridgeView - Integrates cursor hook, refactors keypad change handling, adds handleSourcePresetAmountSelect and handleFlipTokensPress with cursor reset
  5. Modified TokenInputArea - Adds controlled selection and onSelectionChange props
  6. Modified GaslessQuickPickOptions - Refactored to accept tokenBalance as prop (lifted state), changed callback interface from onChange to onAmountSelect

These changes affect the Bridge/Swap flow UX (amount input, quick-pick presets, flip tokens button). The SmokeTrade tag covers bridge and swap flows. Per the tag description, SmokeTrade requires SmokeConfirmations since transaction confirmations are part of the swap/bridge flow.

No changes to: core controllers, Engine, navigation, account management, network management, identity/sync, or any other feature areas. No performance-critical code paths (no list rendering, no startup code, no state management infrastructure).

Performance Test Selection:
Changes are limited to Bridge UI components - cursor position tracking, amount normalization, and quick-pick preset refactoring. These are UI interaction improvements with no impact on rendering performance, list loading, app startup, or data fetching. No performance test tags are warranted.

View GitHub Actions results

@github-actions

Copy link
Copy Markdown
Contributor

E2E Fixture Validation — Schema is up to date
16 value mismatches detected (expected — fixture represents an existing user).
View details

@sonarqubecloud

Copy link
Copy Markdown

@GeorgeGkas

Copy link
Copy Markdown
Contributor
| **Description**                                                                                        | **Input**   | **Key(s) Pressed** | **Cursor position** | **Expected** | **iOS Actual** | **Android Actual** | **iOS Result** |                                                   **Android Result**                                                   |
|--------------------------------------------------------------------------------------------------------|-------------|--------------------|---------------------|--------------|----------------|--------------------|:--------------:|:----------------------------------------------------------------------------------------------------------------------:|
| adding the value 1 into empty input appends at the end                                                 | ""          | 1                  | 0                   | 1            | 1              | 1                  |||
| adding the value 1. into empty input appends at the end                                                | ""          | 1.                 | 0                   | 1.           | 1.             | 1.                 |||
| adding the value 1.2 into empty input appends at the end                                               | ""          | 1.2                | 0                   | 1.2          | 1.2            | 1.2                |||
| removing last character from a value                                                                   | "1.2"       | <Backspace>        | 3                   | 1.           | 1.             | 1.                 |||
| adding (dot) character at the end of an integer does not break value                                   | "23"        | .                  | 2                   | 23.          | 23.            | 23.                |||
| removing (dot) character at the end of an integer does not break value                                 | "23."       | <Backspace>        | 3                   | 23           | 23             | 23                 |||
| adding (dot) as first character appends to zero                                                        | ""          | .                  | 0                   | 0.           | 0.             | 0.                 |||
| should not add second (dot) immediately after a (dot)                                                  | "0."        | .                  | 2                   | 0.           | 0.             | 0.                 |||
| should not add second (dot) in decimal if already present                                              | "0.0234"    | .                  | 6                   | 0.0234       | 0.0234         | 0.0234             |||
| should not add second (dot) in decimal if already present                                              | "0.0234"    | .                  | 2                   | 0.0234       | 0.0234         | 0.0234             |||
| should not add second (dot) in decimal if already present                                              | "0.0234"    | .                  | 0                   | 0.0234       | 0.0234         | 0.0234             |||
| deleting (dot) when number is zero should result in a single zero                                      | "0.00000"   | <Backspace>        | 3                   | 0            | 0              | 0                  |||
| should delete correct character in integer value when cursor is place inside                           | "2,365"     | <Backspace>        | 3                   | 235          | 235            | 235                |||
| should delete correct character in integer value when cursor is place inside                           | "2,365"     | <Backspace>        | 1                   | 265          | 265            | 265                |||
| removing (dot) at the middle of a number should turn the number into integer and not adjust formatting | "5,899.58"  | <Backspace>        | 5                   | 589,958      | 589,958        | 589,958            |||
| adding (dot) at the middle of a number should turn the number into integer and not adjust formatting   | "589,958"   | .                  | 5                   | 58,995.8     | 58,995.8       | 58,995.8           |||
| adding (dot) at formatted position where comma exist, should update the value and reformat             | "1,556,234" | .                  | 4                   | 1,556.234    | 1,556.234      | 1,556.234          |||
| pressing keypad numbers quickly should always append to last cursor position                           | ""          | 745347878          | 0                   | 745,347,878  | 745,347,878    | 745,347,878        || ⚠️ Sometimes the cursor position moves around when pressing keypads quickly and do not wait a bit between each keypress |

@bfullam bfullam enabled auto-merge March 20, 2026 10:35
@GeorgeGkas GeorgeGkas self-requested a review March 20, 2026 10:40
@bfullam bfullam added this pull request to the merge queue Mar 20, 2026
Merged via the queue into main with commit 969c36d Mar 20, 2026
94 of 96 checks passed
@bfullam bfullam deleted the swaps-input-cursor branch March 20, 2026 11:01
@github-actions github-actions Bot locked and limited conversation to collaborators Mar 20, 2026
@metamaskbot metamaskbot added the release-7.72.0 Issue or pull request that will be included in release 7.72.0 label Mar 20, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

release-7.72.0 Issue or pull request that will be included in release 7.72.0 risk-medium Moderate testing recommended · Possible bug introduction risk size-XL team-swaps-and-bridge Swaps and Bridge team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants