feat: FormTextField migration#1197
Conversation
📖 Storybook Preview |
📖 Storybook Preview |
📖 Storybook Preview |
There was a problem hiding this comment.
Solid implementation 🔥 the discriminated union enforcing id when label is present is a nice accessibility touch, FormTextFieldInnerTextFieldProps preventing textFieldProps duplication is well thought through, and the migration docs are thorough. Approved with option to merge and create follow-up PRs or address in this one up to you.
| import type { FormTextFieldProps } from './FormTextField.types'; | ||
| import README from './README.mdx'; | ||
|
|
||
| function ControlledFormTextField(props: FormTextFieldProps) { |
There was a problem hiding this comment.
non-blocking: Same feedback as the TextFieldSearch PR — the Default story already manages state inline correctly, worth extending that pattern to Label, HelpText, IsError, IsDisabled, and Size instead of routing through ControlledFormTextField. Each story should show exactly what a consumer would copy-paste.
| label="Amount" | ||
| value="Read-only value" | ||
| /> | ||
| ), |
There was a problem hiding this comment.
non-blocking: Layout wrapper here could be a Box instead of a raw <div> per the styling conventions.
| ); | ||
|
|
||
| const root = screen.getByTestId(ROOT_TEST_ID); | ||
| const fieldContainer = root.querySelector('.inline-flex'); |
There was a problem hiding this comment.
suggestion: .querySelector('.inline-flex') and the h-12 assertion test TextField's internal implementation rather than FormTextField's contract. The size forwarding is already covered by TextField's own tests — removing this test or replacing the selector with a data-testid on the TextField root would make it less brittle. The same applies to the textFieldProps.className test at line 157.
|
|
||
| type Story = StoryObj<FormTextFieldProps>; | ||
|
|
||
| export const Default: Story = { |
There was a problem hiding this comment.
non-blocking: The Default story (label="Amount", no helpText) and the README quick-start example (label="Email", helpText="We never share your email.") are out of sync with each other. The README code block should be the Default story verbatim. Worth aligning both to a single representative example that includes helpText — the extension's default story shows label + placeholder + help text together and is a good reference: https://metamask.github.io/metamask-storybook/?path=/docs/components-componentlibrary-formtextfield--docs (will attach screenshot).
📖 Storybook Preview |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ 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 f21ecdd. Configure here.
| className={className} | ||
| style={style} | ||
| {...rest} | ||
| > |
There was a problem hiding this comment.
Top-level onClick not forwarded
Medium Severity
FormTextField inherits onClick from TextFieldProps but does not destructure or pass it to the inner TextField. It remains in ...rest and is applied to the root Box, so clicks on the label or help text invoke the handler, and clicks on a disabled field can still fire it because TextField’s disabled guard never runs.
Reviewed by Cursor Bugbot for commit f21ecdd. Configure here.
## Release 43.0.0 This release drops Node.js 18 support across the release line, adds several new components, and includes a small set of breaking API changes that are documented in the migration guides. ### 📦 Package Versions - `@metamask/design-system-shared`: **0.21.0** - `@metamask/design-system-react`: **0.25.0** - `@metamask/design-system-react-native`: **0.28.0** - `@metamask/design-tokens`: **8.5.0** - `@metamask/design-system-tailwind-preset`: **0.9.0** - `@metamask/design-system-twrnc-preset`: **0.5.0** ### 🔄 Shared Type Updates (0.21.0) #### Added - Added `ContentPropsShared` and `ContentVerticalAlignment` for React Native list-style rows and related layout patterns ([#1192](#1192)) #### Changed - **BREAKING:** Dropped Node.js 18 support for the release line; consumers must run Node 20 or newer ([#1206](#1206)) - **BREAKING:** Updated `TextAreaPropsShared` to remove `inputElement` so React Native `TextArea` can render the root `TextInput` directly ([#1205](#1205)) ### 🌐 React Web Updates (0.25.0) #### Added - Added `Popover` for anchored overlays such as menus, tooltips, and dialogs ([#1153](#1153)) - Added `TextArea` for controlled multiline text entry ([#1036](#1036)) - Added `TextFieldSearch` for controlled search-field flows on top of `TextField` ([#1171](#1171)) - Added `FormTextField` for labeled form controls built from `Label`, `TextField`, and `HelpText` ([#1197](#1197)) #### Changed - **BREAKING:** Dropped Node.js 18 support for the release line; consumers must run Node 20 or newer ([#1206](#1206)) - Updated avatar fallback handling so `AvatarToken`, `AvatarNetwork`, and `AvatarFavicon` resolve consistently when the requested image is unavailable ([#1212](#1212)) ### 📱 React Native Updates (0.28.0) #### Added - Added `Content` for composing scrollable and padded content sections on React Native screens; it is closely related to the upcoming `ListItem` work ([#1192](#1192)) #### Changed - **BREAKING:** Dropped Node.js 18 support for the release line; consumers must run Node 20 or newer ([#1206](#1206)) - Added default padding and `isInteractive` support to `SectionHeader` so section rows match the new mobile layout patterns ([#1210](#1210)) - **BREAKING:** Flattened `TextArea` so it renders the root `TextInput` directly; pass `TextInput` props on `TextArea`, use the component `ref` for the input, and stop relying on `inputProps` or `inputElement` ([#1205](#1205)) - Updated avatar fallback handling so `AvatarToken`, `AvatarNetwork`, and `AvatarFavicon` resolve consistently when the requested image is unavailable ([#1212](#1212)) ###⚠️ Breaking Changes #### Node.js 18 support removed **What Changed:** - The release line now requires Node 20 or newer. - This applies across the monorepo, including the shared package, web package, React Native package, tokens, and both preset packages. **Impact:** - Consumers still on Node 18 must upgrade their runtime before installing or developing against this release line. - Node 18 is end-of-life, so this change aligns the repo with the supported app runtimes. #### React Native `TextArea` flattening **What Changed:** - `TextArea` now renders the root `TextInput` directly. - `inputProps` and `inputElement` are removed. - `inputRef` is replaced by the component `ref`. **Migration:** ```tsx // Before (0.27.0) <TextArea inputProps={{ placeholder: 'Message' }} inputElement={<CustomInput />} /> // After (0.28.0) <TextArea placeholder="Message" ref={inputRef} /> ``` **Impact:** - Affects React Native consumers using `TextArea`. - Call sites that depended on the wrapper/input split need to be updated. See migration guides for complete instructions: - [React Migration Guide](./packages/design-system-react/MIGRATION.md#from-version-0220-to-0230) - [React Native Migration Guide](./packages/design-system-react-native/MIGRATION.md#from-version-0270-to-0280) ### ✅ 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.20.0 → 0.21.0) - shared type additions and breaking TextArea/shared runtime baseline - [x] design-system-react: minor (0.24.0 → 0.25.0) - new components and release-line update - [x] design-system-react-native: minor (0.27.0 → 0.28.0) - new component, SectionHeader update, and breaking TextArea change - [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** - [x] 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 - [ ] All tests pass (`yarn build && yarn test && yarn lint`) - [x] Changelog validation passes (`yarn changelog:validate`) ## **Pre-merge reviewer checklist** - [x] I've reviewed the [Reviewing Release PRs](./docs/reviewing-release-prs.md) guide - [x] Package versions follow semantic versioning - [x] Changelog entries are consumer-facing (not commit message regurgitation) - [x] Breaking changes are documented in MIGRATION.md with examples - [x] All unreleased changes are accounted for in changelogs <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Breaking React Native TextArea and Node 18 removal affect consumer upgrades; most diff is release metadata with coordinated peer dependency bumps. > > **Overview** > **Release 43.0.0** bumps the monorepo root to **43.0.0** and publishes coordinated semver bumps across design-system packages, with **yarn.lock** peer ranges updated for `@metamask/design-system-tailwind-preset` **^0.9.0** and `@metamask/design-system-twrnc-preset` **^0.5.0**. > > Across the release line, changelogs record **Node.js 18 dropped** (Node **20+** required). **@metamask/design-system-react** **0.25.0** documents new **`Popover`**, **`TextArea`**, **`TextFieldSearch`**, and **`FormTextField`**, plus avatar fallback fixes. **@metamask/design-system-react-native** **0.28.0** adds **`Content`**, updates **`SectionHeader`** (default padding, **`isInteractive`**), and includes a **breaking** **`TextArea`** flattening (`inputProps` / `inputElement` / `inputRef` removed; props and **`ref`** target the root **`TextInput`**). **@metamask/design-system-shared** **0.21.0** adds **`ContentPropsShared`** / **`ContentVerticalAlignment`** and removes **`inputElement`** from shared **`TextArea`** props. > > Migration guide edits in this diff: React Native **0.27.0 → 0.28.0** **`TextArea`** guidance; React version heading **0.22.0 → 0.23.0** for **`BannerBase`** (changelog-driven **0.25.0** items are not new migration sections here). > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 23b0cda. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->


Description
Added
FormTextFieldcomponent to DSR.Related issues
Fixes: https://consensyssoftware.atlassian.net/browse/DSYS-311
Manual testing steps
Screenshots/Recordings
Before
After
Screen.Recording.2026-06-01.at.14.11.12.mov
Pre-merge author checklist
Pre-merge reviewer checklist
Note
Low Risk
New UI primitive with tests and migration guidance; no auth, data, or runtime behavior changes outside the design-system package.
Overview
Adds
FormTextFieldto@metamask/design-system-reactas a labeled form control built fromLabel,TextField, and optionalHelpText, withisErrordriving danger styling andaria-invalidon the input.The API uses
isDisabled/isReadOnly/isError,TextFieldSize(no separateFormTextFieldSize), a fixed column root viaBox, and discriminated types soidis required whenlabelis set.labelProps,helpTextProps, andtextFieldPropsforward extras; rootrefvsinputRefmatchTextField.MIGRATION.mddocuments extension → design-system moves (imports, prop renames, removed polymorphic/defaultValuebehavior).Ships unit tests, Storybook stories/README, and a public export from the package index.
Reviewed by Cursor Bugbot for commit f21ecdd. Bugbot is set up for automated code reviews on this repo. Configure here.