Skip to content

refactor: updated Input and TextField to align with mobile fixes#1000

Merged
brianacnguyen merged 9 commits into
mainfrom
fix/inputs
Mar 27, 2026
Merged

refactor: updated Input and TextField to align with mobile fixes#1000
brianacnguyen merged 9 commits into
mainfrom
fix/inputs

Conversation

@brianacnguyen

@brianacnguyen brianacnguyen commented Mar 25, 2026

Copy link
Copy Markdown
Contributor

Description

Aligns React Native Input and TextField with the mobile product fixes: more predictable single-line text layout and a single, spec-sized text field row.

Input

  • Stops using text-${textVariant} Tailwind classes for typography and applies font size and letter spacing from design tokens via new MAP_TEXT_VARIANT_INPUT_METRICS in Input.constants.ts, without paragraph lineHeight so TextInput vertical alignment matches the intended legacy behavior.
  • Keeps the iOS placeholder lineHeight: 0 workaround only when the placeholder is visible.
  • Removes the unconditional iOS textAlignVertical: 'center' style.
  • Storybook: wraps interactive stories in a small controlled helper so typing works with required value.

TextField

  • Breaking: removes TextFieldSize and the size prop; the field is always a 48px row (h-12) with a 46px inner Input so the bordered row does not overflow.
  • Container uses bg-muted and state-based border colors (muted at rest; default when focused; error / primary combinations for error + focus; muted when disabled).
  • Inner Input is single-line (numberOfLines={1}, multiline={false}).
  • Accessory spacing is mr-3 / ml-3; adds startAccessoryTestID and endAccessoryTestID on the accessory wrapper views for E2E.
  • README, package exports, tests, and stories updated accordingly.

Related issues

Fixes: https://consensyssoftware.atlassian.net/browse/DSYS-654

Manual testing steps

  1. From the repo root, run React Native Storybook (yarn storybook:ios or yarn storybook:android).
  2. Open Components → Input: confirm variants, disabled/readonly, and that typing works in controlled stories; on iOS, check placeholder vs. filled text vertical alignment.
  3. Open Components → TextField: confirm 48px height, muted background, border colors for default / focus / error / disabled, and start/end accessories; if you use E2E, confirm startAccessoryTestID / endAccessoryTestID resolve.

Screenshots/Recordings

Before

Input / TextField before this branch (e.g. old sizes, old borders/background, any misaligned placeholder).

After

Simulator.Screen.Recording.-.iPhone.15.Pro.Max.-.2026-03-24.at.23.57.08.mov

Pre-merge author checklist

  • I've followed MetaMask Contributor Docs
  • I've completed the PR template to the best of my ability
  • I’ve included tests if applicable
  • I’ve documented my code using JSDoc format if applicable
  • I’ve applied the right labels on the PR (see labeling guidelines). Not required for external contributors.

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
Medium risk because it introduces a breaking API change (removes TextFieldSize/size) and alters text/layout styling that could affect UI rendering across platforms.

Overview
Updates React Native Input typography to use token-driven fontSize/letterSpacing (without Tailwind text-* line heights) and narrows the iOS placeholder line-height workaround to placeholder-visible state.

Refactors TextField to a single fixed 48px row (46px inner Input), removes TextFieldSize and related exports, and updates container visuals (muted background, gap-3 accessory spacing, and revised focus/error/disabled border behavior) while forcing single-line input.

Refreshes Storybook stories to use controlled wrappers, updates unit tests to match the new layout/state styling, and documents the breaking change in MIGRATION.md/TextField README.

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

@brianacnguyen brianacnguyen requested a review from a team as a code owner March 25, 2026 19:08
@brianacnguyen brianacnguyen marked this pull request as draft March 25, 2026 19:08
@github-actions

Copy link
Copy Markdown
Contributor

📖 Storybook Preview

@github-actions

Copy link
Copy Markdown
Contributor

📖 Storybook Preview

@brianacnguyen brianacnguyen marked this pull request as ready for review March 26, 2026 20:48

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

Looks good! Could we avoid adding startAccessoryTestID / endAccessoryTestID as new props. If we want to reduce wrapper Views in a future refactor, these props will force us to keep (or replace) the wrappers and can create extra breaking-change surface. For test we can put thetestID on the accessory element itself (or let consumers wrap accessories as needed).

Reference: the extension TextField implementation is materially less wrapper-heavy: https://github.com/MetaMask/metamask-extension/blob/main/ui/components/component-library/text-field/text-field.tsx#L131

/**
* Optional test ID for the start accessory wrapper (when `startAccessory` is set).
*/
startAccessoryTestID?: string;

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.

suggestion: Can we drop startAccessoryTestID / endAccessoryTestID? Adding wrapper-specific testID props makes it harder to remove wrapper Views later without additional breaking changes. If an E2E test needs a stable selector, we can pass testID directly on the accessory element (or let consumers wrap the accessory). See extension TextField for a less wrapper-heavy approach: https://github.com/MetaMask/metamask-extension/blob/main/ui/components/component-library/text-field/text-field.tsx#L131

Comment thread packages/design-system-react-native/src/components/TextField/TextField.tsx Outdated

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

Whoops duplicate

@github-actions

Copy link
Copy Markdown
Contributor

📖 Storybook Preview

@github-actions

Copy link
Copy Markdown
Contributor

📖 Storybook Preview

@github-actions

Copy link
Copy Markdown
Contributor

📖 Storybook Preview

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

LGTM! Left a non-blocking question about the error and focused state

Comment thread packages/design-system-react-native/src/components/TextField/README.md Outdated
Comment thread packages/design-system-react-native/src/components/TextField/TextField.tsx Outdated
@github-actions

Copy link
Copy Markdown
Contributor

📖 Storybook Preview

@github-actions

Copy link
Copy Markdown
Contributor

📖 Storybook Preview

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

Nice work! 🚀

@brianacnguyen brianacnguyen merged commit 1304e92 into main Mar 27, 2026
44 checks passed
@brianacnguyen brianacnguyen deleted the fix/inputs branch March 27, 2026 00:26
@georgewrmarshall georgewrmarshall mentioned this pull request Mar 27, 2026
18 tasks
georgewrmarshall added a commit that referenced this pull request Mar 27, 2026
## Release 26.0.0

This release adds new BannerAlert components, introduces `KeyValueRow`
for React Native, includes breaking simplifications to React Native
`TextButton` and `TextField`, and continues ADR-0003/0004 shared type
migrations.

### 📦 Package Versions

- `@metamask/design-system-shared`: **0.5.0**
- `@metamask/design-system-react`: **0.12.0**
- `@metamask/design-system-react-native`: **0.12.0**

### 🔄 Shared Type Updates (0.5.0)

#### Component Type Additions (#975, #997)

**What Changed:**

- Added `BannerAlertSeverity` const object + `BannerAlertPropsShared`
- Added `AvatarNetworkPropsShared` type (ADR-0004)

**Impact:**

- Enables consistent cross-platform `BannerAlert` severity/types
- Continues ADR-0003/ADR-0004 const-object + string-union + shared types
adoption

### 🌐 React Web Updates (0.12.0)

#### Added

- Added `BannerAlert` component (#975)

#### Changed

- Updated `TextButton` hover/pressed styles to be text-only (no
background fill) (#1001)
- Updated `Candlestick` icon asset with smaller size variant (#998)

### 📱 React Native Updates (0.12.0)

#### Added

- Added `BannerAlert` component (#966)
- Added `KeyValueRow` component (#959)

#### Changed

- **BREAKING:** Simplified `TextButton` to a text-only control and
removed `size`/`TextButtonSize`, inverse/disabled props, and
icon/accessory props (#1001)
- Migration:
`./packages/design-system-react-native/MIGRATION.md#from-version-0110-to-0120`
- **BREAKING:** Removed `TextFieldSize` and the `size` prop; `TextField`
is now a single fixed-height (48px) row (#1000)
- Migration:
`./packages/design-system-react-native/MIGRATION.md#from-version-0110-to-0120`
- Updated `Candlestick` icon asset with smaller size variant (#998)

#### Fixed

- Improved `Input` single-line typography alignment (including iOS
placeholder behavior) (#1000)

### ⚠️ Breaking Changes

#### TextButton API (React Native)

**What Changed:**

- Removed `size`/`TextButtonSize`, `isDisabled`, `isInverse`, and
start/end icon & accessory props
- `TextButton` now aligns with `Text` typography and press handlers
(`variant`, `fontWeight`, `onPress`, etc.)

**Migration:**

See
`./packages/design-system-react-native/MIGRATION.md#from-version-0110-to-0120`.

**Impact:**

- Affects all React Native `TextButton` usages relying on the removed
props

#### TextField Size API (React Native)

**What Changed:**

- Removed `TextFieldSize` and the `size` prop
- `TextField` is now a single fixed-height (48px) row

**Migration:**

See
`./packages/design-system-react-native/MIGRATION.md#from-version-0110-to-0120`.

**Impact:**

- Affects all React Native `TextField` usages passing `size` / importing
`TextFieldSize`

### ✅ Checklist

- [x] Changelogs updated with human-readable descriptions
- [x] Changelog validation passed (`yarn changelog:validate`)
- [x] Version bumps follow semantic versioning
  - [x] design-system-shared: minor (0.4.0 → 0.5.0) - new shared types
- [x] design-system-react: minor (0.11.0 → 0.12.0) - new component +
non-breaking changes
- [x] design-system-react-native: minor (0.11.0 → 0.12.0) - new
components + breaking API changes (pre-1.0)
- [x] Breaking changes documented with migration guidance
- [x] Migration guides updated with before/after examples (if breaking
changes)
- [x] PR references included in changelog entries

## **Pre-merge author checklist**

- [ ] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs)
- [x] I've reviewed the [Release
Workflow](./.cursor/rules/release-workflow.md) cursor rule
- [x] All tests pass (`yarn build && yarn test && yarn lint`)
- [x] Changelog validation passes (`yarn changelog:validate`)

## **Pre-merge reviewer checklist**

- [ ] I've reviewed the [Reviewing Release
PRs](./docs/reviewing-release-prs.md) guide
- [ ] Package versions follow semantic versioning
- [ ] Changelog entries are consumer-facing (not commit message
regurgitation)
- [ ] Breaking changes are documented in MIGRATION.md with examples
- [ ] All unreleased changes are accounted for in changelogs

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Mostly a version/changelog release PR, but includes documented
**breaking** React Native API removals for `TextButton` and `TextField`
that can break downstream consumers on upgrade.
> 
> **Overview**
> Bumps the monorepo version to `26.0.0` and releases
`@metamask/design-system-react`/`@metamask/design-system-react-native`
to `0.12.0` and `@metamask/design-system-shared` to `0.5.0`.
> 
> Updates changelogs and the React Native migration guide to reflect new
`BannerAlert` (web + RN) and `KeyValueRow` (RN), shared type additions
(`BannerAlertSeverity`/`BannerAlertPropsShared`,
`AvatarNetworkPropsShared`), and **breaking** RN API changes removing
`TextButton` sizing/icons/disabled/inverse props and removing
`TextField` sizing (`TextFieldSize`/`size`) in favor of a fixed-height
row.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
5ead00c. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
georgewrmarshall pushed a commit that referenced this pull request Apr 27, 2026
## **Description**

Aligns React Native **Input** and **TextField** with the mobile product
fixes: more predictable single-line text layout and a single, spec-sized
text field row.

**Input**

- Stops using `text-${textVariant}` Tailwind classes for typography and
applies **font size and letter spacing from design tokens** via new
`MAP_TEXT_VARIANT_INPUT_METRICS` in `Input.constants.ts`, **without**
paragraph `lineHeight` so `TextInput` vertical alignment matches the
intended legacy behavior.
- Keeps the **iOS placeholder** `lineHeight: 0` workaround only when the
placeholder is visible.
- Removes the unconditional iOS `textAlignVertical: 'center'` style.
- **Storybook:** wraps interactive stories in a small **controlled**
helper so typing works with required `value`.

**TextField**

- **Breaking:** removes **`TextFieldSize`** and the `size` prop; the
field is always a **48px** row (`h-12`) with a **46px** inner `Input` so
the bordered row does not overflow.
- Container uses **`bg-muted`** and **state-based border colors** (muted
at rest; default when focused; error / primary combinations for error +
focus; muted when disabled).
- Inner `Input` is **single-line** (`numberOfLines={1}`,
`multiline={false}`).
- Accessory spacing is **`mr-3` / `ml-3`**; adds
**`startAccessoryTestID`** and **`endAccessoryTestID`** on the accessory
wrapper views for E2E.
- README, package exports, tests, and stories updated accordingly.

## **Related issues**

Fixes: https://consensyssoftware.atlassian.net/browse/DSYS-654

## **Manual testing steps**

1. From the repo root, run React Native Storybook (`yarn storybook:ios`
or `yarn storybook:android`).
2. Open **Components → Input**: confirm variants, disabled/readonly, and
that typing works in controlled stories; on **iOS**, check placeholder
vs. filled text vertical alignment.
3. Open **Components → TextField**: confirm 48px height, muted
background, border colors for default / focus / error / disabled, and
start/end accessories; if you use E2E, confirm `startAccessoryTestID` /
`endAccessoryTestID` resolve.

## **Screenshots/Recordings**

### **Before**

_Input / TextField before this branch (e.g. old sizes, old
borders/background, any misaligned placeholder)._

### **After**

https://github.com/user-attachments/assets/dc37ed5c-3bcc-484e-8677-fd74684fcdf9


## **Pre-merge author checklist**

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs)
- [x] I've completed the PR template to the best of my ability
- [x] I’ve included tests if applicable
- [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [x] I’ve applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.

## **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.


<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Medium risk because it introduces a breaking API change (removes
`TextFieldSize`/`size`) and alters text/layout styling that could affect
UI rendering across platforms.
> 
> **Overview**
> Updates React Native `Input` typography to use token-driven
`fontSize`/`letterSpacing` (without Tailwind `text-*` line heights) and
narrows the iOS placeholder line-height workaround to
placeholder-visible state.
> 
> Refactors `TextField` to a single fixed **48px** row (46px inner
`Input`), removes `TextFieldSize` and related exports, and updates
container visuals (muted background, `gap-3` accessory spacing, and
revised focus/error/disabled border behavior) while forcing single-line
input.
> 
> Refreshes Storybook stories to use controlled wrappers, updates unit
tests to match the new layout/state styling, and documents the breaking
change in `MIGRATION.md`/`TextField` README.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
dc44871. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
georgewrmarshall added a commit that referenced this pull request Apr 27, 2026
## Release 26.0.0

This release adds new BannerAlert components, introduces `KeyValueRow`
for React Native, includes breaking simplifications to React Native
`TextButton` and `TextField`, and continues ADR-0003/0004 shared type
migrations.

### 📦 Package Versions

- `@metamask/design-system-shared`: **0.5.0**
- `@metamask/design-system-react`: **0.12.0**
- `@metamask/design-system-react-native`: **0.12.0**

### 🔄 Shared Type Updates (0.5.0)

#### Component Type Additions (#975, #997)

**What Changed:**

- Added `BannerAlertSeverity` const object + `BannerAlertPropsShared`
- Added `AvatarNetworkPropsShared` type (ADR-0004)

**Impact:**

- Enables consistent cross-platform `BannerAlert` severity/types
- Continues ADR-0003/ADR-0004 const-object + string-union + shared types
adoption

### 🌐 React Web Updates (0.12.0)

#### Added

- Added `BannerAlert` component (#975)

#### Changed

- Updated `TextButton` hover/pressed styles to be text-only (no
background fill) (#1001)
- Updated `Candlestick` icon asset with smaller size variant (#998)

### 📱 React Native Updates (0.12.0)

#### Added

- Added `BannerAlert` component (#966)
- Added `KeyValueRow` component (#959)

#### Changed

- **BREAKING:** Simplified `TextButton` to a text-only control and
removed `size`/`TextButtonSize`, inverse/disabled props, and
icon/accessory props (#1001)
- Migration:
`./packages/design-system-react-native/MIGRATION.md#from-version-0110-to-0120`
- **BREAKING:** Removed `TextFieldSize` and the `size` prop; `TextField`
is now a single fixed-height (48px) row (#1000)
- Migration:
`./packages/design-system-react-native/MIGRATION.md#from-version-0110-to-0120`
- Updated `Candlestick` icon asset with smaller size variant (#998)

#### Fixed

- Improved `Input` single-line typography alignment (including iOS
placeholder behavior) (#1000)

### ⚠️ Breaking Changes

#### TextButton API (React Native)

**What Changed:**

- Removed `size`/`TextButtonSize`, `isDisabled`, `isInverse`, and
start/end icon & accessory props
- `TextButton` now aligns with `Text` typography and press handlers
(`variant`, `fontWeight`, `onPress`, etc.)

**Migration:**

See
`./packages/design-system-react-native/MIGRATION.md#from-version-0110-to-0120`.

**Impact:**

- Affects all React Native `TextButton` usages relying on the removed
props

#### TextField Size API (React Native)

**What Changed:**

- Removed `TextFieldSize` and the `size` prop
- `TextField` is now a single fixed-height (48px) row

**Migration:**

See
`./packages/design-system-react-native/MIGRATION.md#from-version-0110-to-0120`.

**Impact:**

- Affects all React Native `TextField` usages passing `size` / importing
`TextFieldSize`

### ✅ Checklist

- [x] Changelogs updated with human-readable descriptions
- [x] Changelog validation passed (`yarn changelog:validate`)
- [x] Version bumps follow semantic versioning
  - [x] design-system-shared: minor (0.4.0 → 0.5.0) - new shared types
- [x] design-system-react: minor (0.11.0 → 0.12.0) - new component +
non-breaking changes
- [x] design-system-react-native: minor (0.11.0 → 0.12.0) - new
components + breaking API changes (pre-1.0)
- [x] Breaking changes documented with migration guidance
- [x] Migration guides updated with before/after examples (if breaking
changes)
- [x] PR references included in changelog entries

## **Pre-merge author checklist**

- [ ] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs)
- [x] I've reviewed the [Release
Workflow](./.cursor/rules/release-workflow.md) cursor rule
- [x] All tests pass (`yarn build && yarn test && yarn lint`)
- [x] Changelog validation passes (`yarn changelog:validate`)

## **Pre-merge reviewer checklist**

- [ ] I've reviewed the [Reviewing Release
PRs](./docs/reviewing-release-prs.md) guide
- [ ] Package versions follow semantic versioning
- [ ] Changelog entries are consumer-facing (not commit message
regurgitation)
- [ ] Breaking changes are documented in MIGRATION.md with examples
- [ ] All unreleased changes are accounted for in changelogs

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Mostly a version/changelog release PR, but includes documented
**breaking** React Native API removals for `TextButton` and `TextField`
that can break downstream consumers on upgrade.
> 
> **Overview**
> Bumps the monorepo version to `26.0.0` and releases
`@metamask/design-system-react`/`@metamask/design-system-react-native`
to `0.12.0` and `@metamask/design-system-shared` to `0.5.0`.
> 
> Updates changelogs and the React Native migration guide to reflect new
`BannerAlert` (web + RN) and `KeyValueRow` (RN), shared type additions
(`BannerAlertSeverity`/`BannerAlertPropsShared`,
`AvatarNetworkPropsShared`), and **breaking** RN API changes removing
`TextButton` sizing/icons/disabled/inverse props and removing
`TextField` sizing (`TextFieldSize`/`size`) in favor of a fixed-height
row.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
5ead00c. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants