Skip to content

feat: [DSRN] Added ListItem#1203

Merged
brianacnguyen merged 9 commits into
mainfrom
dsrn/listitem
Jun 8, 2026
Merged

feat: [DSRN] Added ListItem#1203
brianacnguyen merged 9 commits into
mainfrom
dsrn/listitem

Conversation

@brianacnguyen

@brianacnguyen brianacnguyen commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Description

Refactors @metamask/design-system-react-native ListItem from a generic horizontal child wrapper into a padded list row built on Content.

Why: ListItem should match the design system's standard row layout (title, description, value, avatar, accessories) and support tappable rows with consistent press feedback. The previous API laid out arbitrary children in a row with gap spacers, which didn't align with how list rows are used in product.

What changed:

  • ListItem now wraps Content inside a padded root (px-4 py-3).
  • isInteractive toggles the root between Box (default) and Pressable (applies bg-pressed while pressed).
  • Row content is configured via Content props (title, description, value, subvalue, avatar, accessories, etc.).
  • Optional children render below the Content block (e.g. expanded details), not as siblings in the main row.
  • Adds ListItemPropsShared in @metamask/design-system-shared (ADR-0004) for cross-platform props (isInteractive, children).

Breaking changes:

  • Removed ListItemVerticalAlignment — use ContentVerticalAlignment from shared instead.
  • Removed gap, topAccessoryGap, and bottomAccessoryGap.
  • Removed ListItem.constants.ts and the horizontal child + spacer layout model.
  • ListItemVerticalAlignment is no longer exported from @metamask/design-system-react-native.

For row layout without padding or press handling, use Content directly.

Related issues

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

Manual testing steps

  1. Run React Native Storybook: yarn storybook:ios (or yarn storybook:android).
  2. Open Components/ListItem and verify:
    • Default — title, description, and value render correctly.
    • IsInteractive — row is tappable and shows pressed feedback.
    • StartAccessory / EndAccessory / TopAccessory / Avatar — accessories and avatar render in the expected positions.
    • VerticalAlignment — Center and Top rows show correct alignment and guidance copy.
    • Children — extra content renders below the main row.
  3. Run unit tests: yarn workspace @metamask/design-system-react-native run jest --testPathPattern=ListItem.test --watchman=false

Screenshots/Recordings

Before

After

Simulator.Screen.Recording.-.iPhone.15.Pro.Max.-.2026-06-02.at.23.03.17.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

High Risk
Large breaking API change to a widely used list primitive (~58 mobile call sites per migration doc); consumers must migrate from children/gap layout to Content props and ContentVerticalAlignment.

Overview
ListItem is rebuilt as a padded list row (px-4 py-3) around Content, replacing the old horizontal children + gap spacer layout.

API: Row text and chrome use Content props (title, description, value, subvalue, avatar, start/end/top/bottom accessories). isInteractive switches the root from Box to Pressable with default bg-pressed feedback and accessibilityRole="button". Optional children render below Content, not in the main row. ListItemPropsShared (isInteractive, children) is added in @metamask/design-system-shared.

Breaking: ListItemVerticalAlignment is removed—use ContentVerticalAlignment (Bottom has no equivalent). gap, topAccessoryGap, bottomAccessoryGap, and ListItem.constants.ts are removed. ListItemVerticalAlignment is no longer exported from the RN package. MIGRATION.md documents mobile → design-system mappings.

Tests, Storybook, and README are updated for the new model.

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

@brianacnguyen brianacnguyen requested a review from a team as a code owner June 1, 2026 19:44
@brianacnguyen brianacnguyen marked this pull request as draft June 1, 2026 19:44
@github-actions

github-actions Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

📖 Storybook Preview

Comment thread packages/design-system-shared/src/utils/compound-slots/useCompoundSlots.ts Outdated
Comment thread packages/design-system-shared/src/utils/compound-slots/useCompoundSlots.ts Outdated
@github-actions

github-actions Bot commented Jun 1, 2026

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.

Really solid work on the component — the Content layout, interactive/non-interactive branching, prop override semantics, and the overall API surface are well thought through. The flat prop API is production-ready and there's no disagreement on the direction: providing both a prop-based API and compound component syntax is the right end state.

The open question — being discussed in MetaMask/decisions#203 — is which mechanism to use for the compound component part (null-marker parsed slots vs render-in-place sub-components). Since that discussion is still live, I'd suggest shipping this PR as a v1 with the flat prop API only and adding compound component support as a follow-up once the approach is settled. This unblocks teams who need ListItem now with a clean, well-tested API:

<ListItem
  avatar={<AvatarToken name="ETH" size="lg" />}
  title="Account 2"
  titleEndAccessory={<Tag>Hardware Wallet</Tag>}
  description="Secondary text"
  value="$5,678.00"
  subvalue="1.2 ETH"
  isInteractive
/>

Nothing in the ADR conversation changes the prop-based API — it's purely additive. Shipping now avoids blocking on a mechanism decision that doesn't affect v1 consumers at all.

Comment thread packages/design-system-react-native/src/components/ListItem/ListItem.slots.ts Outdated
@brianacnguyen brianacnguyen changed the title DRAFT: Added ListItem and compound slots feat: [DSRN] Added ListItem Jun 3, 2026
@github-actions

github-actions Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

📖 Storybook Preview

@brianacnguyen brianacnguyen marked this pull request as ready for review June 3, 2026 06:08
@github-actions

github-actions Bot commented Jun 3, 2026

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.

Clean migration from the generic-wrapper pattern to a Content-backed prop API. The discriminated union on isInteractive is well typed, tests follow the correct RN matchers (toBeOnTheScreen, toHaveStyle(tw.style(...))), and the README is consumer-facing throughout. One item to resolve before merging.

Comment thread packages/design-system-react-native/src/components/index.ts
@github-actions

github-actions Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

📖 Storybook Preview

@github-actions

github-actions Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

📖 Storybook Preview

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

@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 2 potential issues.

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 d60ab8f. Configure here.

value="1.234 ETH"
onPress={() => {}}
/>
</Box>;

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.

README quick start mismatches Default

Low Severity

The README opening example shows an interactive Network row with onPress, while the Storybook Default story uses non-interactive args (title “Label”, description “Secondary text”, value “Value”) with no isInteractive or parent Box.

Additional Locations (1)
Fix in Cursor Fix in Web

Triggered by learned rule: README quick-start examples must match Default story

Reviewed by Cursor Bugbot for commit d60ab8f. Configure here.

@github-actions

github-actions Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

📖 Storybook Preview

@brianacnguyen brianacnguyen merged commit 0e5430b into main Jun 8, 2026
36 checks passed
@brianacnguyen brianacnguyen deleted the dsrn/listitem branch June 8, 2026 16:09
@georgewrmarshall georgewrmarshall mentioned this pull request Jun 9, 2026
18 tasks
georgewrmarshall added a commit that referenced this pull request Jun 9, 2026
## Release 44.0.0

This release adds `Toast`/`Toaster` and `Tag` to React, `ListItem` to
React Native, aligns the `TextButton` API across platforms, and
standardizes severity vocabulary (`Error` → `Danger`) across
`AvatarIcon`, `IconAlert`, and `Tag`.

### 📦 Package Versions

- `@metamask/design-system-shared`: **0.22.0**
- `@metamask/design-system-react`: **0.26.0**
- `@metamask/design-system-react-native`: **0.29.0**

### 🔄 Shared Type Updates (0.22.0)

#### New shared types (#1190, #1203, #1224, #1225)

**What Changed:**

- Added `ToastPropsShared` and `ToastSeverity` for cross-platform
`Toast` support
- Added `ListItemPropsShared` and related types for cross-platform
`ListItem` support
- Added `TextButtonPropsShared` to align `TextButton` API across React
and React Native
- Added `AvatarNetworkSize` as a named export from the shared package

**Impact:**

- Enables consistent `Toast` and `ListItem` implementations across both
platforms
- Continues ADR-0003/0004 const-object + string-union pattern adoption

#### Severity vocabulary: `.Error` → `.Danger`
([#1159](#1159))

**What Changed:**

- `AvatarIconSeverity.Error` → `AvatarIconSeverity.Danger`
- `IconAlertSeverity.Error` → `IconAlertSeverity.Danger`
- `TagSeverity.Error` → `TagSeverity.Danger`

**Impact:**

- Breaking change for any consumer using `.Error` on these three const
objects. Rendered colors are unchanged.

### 🌐 React Web Updates (0.26.0)

#### Added

- Added `Tag` component for categorization and filtering labels
([#1211](#1211))
- Added `Toast` component with `Toaster` provider and imperative
`toast()` API
([#1190](#1190))

#### Changed

- **BREAKING:** `TextButton` API aligned with React Native —
`size`/`TextButtonSize` replaced by `variant`/`TextVariant`;
`isInverse`, `isDisabled`, `textProps`, and icon/accessory props
removed; `asChild` added
([#1224](#1224))
- **BREAKING:** `AvatarIconSeverity.Error` → `AvatarIconSeverity.Danger`
([#1159](#1159))

#### Fixed

- Fixed `Toast` to support `toast()` calls made before `Toaster` mounts
([#1217](#1217))

### 📱 React Native Updates (0.29.0)

#### Added

- Added `ListItem` component for list row layouts
([#1203](#1203))
- Added `Toast` component with `Toaster` provider and imperative
`toast()` API
([#1190](#1190))

#### Changed

- **BREAKING:** `AvatarIconSeverity.Error`, `IconAlertSeverity.Error`,
and `TagSeverity.Error` → `.Danger`
([#1159](#1159))

### ⚠️ Breaking Changes

#### TextButton rewrite (React Web Only)

**What Changed:**

- `size` / `TextButtonSize` removed — use `variant` / `TextVariant`
instead
- `isInverse`, `isDisabled`, `textProps`, start/end icons, and accessory
slots removed
- `asChild` added for semantic link composition

**Migration:**

```tsx
// Before (0.25.0)
import { TextButton, TextButtonSize } from '@metamask/design-system-react';
<TextButton size={TextButtonSize.BodySm}>Learn more</TextButton>

// After (0.26.0)
import { TextButton } from '@metamask/design-system-react';
import { TextVariant } from '@metamask/design-system-shared';
<TextButton variant={TextVariant.BodySm}>Learn more</TextButton>
```

See [React Migration
Guide](./packages/design-system-react/MIGRATION.md#from-version-0250-to-0260)

#### Severity vocabulary: `.Error` → `.Danger` (Both Platforms)

**What Changed:**

- `AvatarIconSeverity.Error` → `AvatarIconSeverity.Danger` (React +
React Native)
- `IconAlertSeverity.Error` → `IconAlertSeverity.Danger` (React Native)
- `TagSeverity.Error` → `TagSeverity.Danger` (React Native)

**Migration:**

```tsx
// Before
<AvatarIcon severity={AvatarIconSeverity.Error} />
<IconAlert severity={IconAlertSeverity.Error} />
<Tag severity={TagSeverity.Error}>High risk</Tag>

// After
<AvatarIcon severity={AvatarIconSeverity.Danger} />
<IconAlert severity={IconAlertSeverity.Danger} />
<Tag severity={TagSeverity.Danger}>High risk</Tag>
```

See migration guides for complete instructions:

- [React Migration
Guide](./packages/design-system-react/MIGRATION.md#from-version-0250-to-0260)
- [React Native Migration
Guide](./packages/design-system-react-native/MIGRATION.md#from-version-0280-to-0290)

### ✅ 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.21.0 → 0.22.0) - new shared types +
breaking severity rename
- [x] design-system-react: minor (0.25.0 → 0.26.0) - new components +
breaking API changes
- [x] design-system-react-native: minor (0.28.0 → 0.29.0) - new
components + breaking severity rename
- [x] Breaking changes documented with migration guidance
- [x] Migration guides updated with before/after examples
- [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**

- [ ] 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**
> The release documents multiple breaking consumer APIs (TextButton on
web, severity `.Error`→`.Danger`); upgrading without following
MIGRATION.md will cause compile/runtime mismatches, though this PR does
not change component implementation itself.
> 
> **Overview**
> **Release 44.0.0** cuts new versions of the design-system packages and
records what shipped since the last release: monorepo root **43.0.0 →
44.0.0**, `@metamask/design-system-shared` **0.22.0**,
`@metamask/design-system-react` **0.26.0**, and
`@metamask/design-system-react-native` **0.29.0**.
> 
> Changelogs document **React** additions (`Tag`,
`Toast`/`Toaster`/`toast()`), a **breaking** `TextButton` rewrite
(`size`/`TextButtonSize` → `variant`/`TextVariant`, dropped
inverse/disabled/icons, added `asChild`), and
**`AvatarIconSeverity.Error` → `.Danger`**. **React Native** adds
**`ListItem`** and the same **severity rename** on `AvatarIcon`,
`IconAlert`, and `Tag`. **Shared** adds cross-platform types
(`ListItem`, `Toast`, `TextButton`, `AvatarNetworkSize`) and the shared
**`.Error` → `.Danger`** severity vocabulary.
> 
> **MIGRATION.md** on React and React Native gains **0.25→0.26** and
**0.28→0.29** sections with before/after examples. The only non-release
code change in the diff is reordering the `react-native-worklets`
devDependency in `apps/storybook-react-native/package.json`.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
9c3345f. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
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.

2 participants