Skip to content

feat: add BannerAlert React and shared types#975

Merged
georgewrmarshall merged 2 commits into
mainfrom
banner-alert-react
Mar 10, 2026
Merged

feat: add BannerAlert React and shared types#975
georgewrmarshall merged 2 commits into
mainfrom
banner-alert-react

Conversation

@georgewrmarshall

@georgewrmarshall georgewrmarshall commented Mar 10, 2026

Copy link
Copy Markdown
Contributor

Description

Adds React implementation and shared types for BannerAlert component, broken out from the complete cross-platform implementation in #966. This PR introduces:

  • Shared types in @metamask/design-system-shared (ADR-0003/0004 aligned)
    • BannerAlertSeverity const object with derived union type
    • BannerAlertPropsShared interface for cross-platform props
  • React implementation in @metamask/design-system-react
    • Severity-driven icon, background, and left-border styling
    • Built on BannerBase with design token mappings
    • Full Storybook stories, unit tests, and component documentation
  • Migration documentation updated in packages/design-system-react/MIGRATION.md

The React Native implementation will follow in a separate PR, allowing for focused review of each platform.

Related issues

Related to:

Manual testing steps

  1. Run yarn build from repo root and verify all workspaces build.
  2. Run yarn lint from repo root and verify lint/format/dependency checks pass.
  3. Run yarn test from repo root and verify package tests pass.
  4. Run React coverage check:
    • NODE_OPTIONS=--experimental-vm-modules ./node_modules/.bin/jest -c packages/design-system-react/jest.config.js --runInBand --watchman=false
  5. Run yarn storybook and verify BannerAlert renders all severities (Info, Success, Warning, Danger) and supports action/close props.

Screenshots/Recordings

Before

N/A

After

Default stories and args (React Web)

banneralert.defautstory.mov

Other stories (React Web)

banner.alert.stories.mov

Docs (React Web)

banneralert.docs.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

Low Risk
Additive UI component/types with isolated styling logic; low risk aside from potential token/icon mapping or export surface issues.

Overview
Adds a new BannerAlert React component that composes BannerBase and applies severity-driven icon, background, and left-border styling, exporting it (and BannerAlertSeverity) from the design system component index.

Introduces shared BannerAlertSeverity and BannerAlertPropsShared in @metamask/design-system-shared, wires them into package exports, and adds Storybook stories, unit tests, and docs for the new component.

Updates MIGRATION.md with extension-to-design-system guidance for BannerAlertSeverity imports/values.

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

@georgewrmarshall georgewrmarshall requested a review from a team as a code owner March 10, 2026 20:25
@github-actions

Copy link
Copy Markdown
Contributor

📖 Storybook Preview

Warning: 'warning',
/** Danger style. */
Danger: 'danger',
} as const;

@georgewrmarshall georgewrmarshall Mar 10, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Follows ADR-0003 pattern for all new components. This replaces TypeScript enums with runtime const objects that have derived union types. Benefits:

  • Better tree-shaking and bundle size
  • Consistent with shared package architecture
  • Simpler cross-package imports (no enum re-export issues)

* BannerAlert shared props (ADR-0004).
* Platform-independent properties shared across React and React Native.
*/
export type BannerAlertPropsShared = BannerBasePropsShared & {

@georgewrmarshall georgewrmarshall Mar 10, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Follows ADR-0004 centralized types architecture. Shared props inherit from BannerBase to get all base banner functionality (title, description, action buttons, close button) while adding only the severity prop. This creates a clear hierarchy: BannerBase provides structure, BannerAlert adds semantic styling.

*/
export type BannerAlertProps = BannerAlertPropsShared &
BannerBaseProps & {
iconProps?: Omit<IconProps, 'name' | 'size' | 'color'>;

@georgewrmarshall georgewrmarshall Mar 10, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Platform-specific customization point. The component internally controls icon name, size, and color based on severity (via the constants mappings), but consumers may need to attach test IDs or additional classes. Omitting the controlled props prevents accidental overrides that would break severity semantics. There may be legitimate use cases for overriding these, let's remove the omission if needed in a subsequent PR. The escape hatch for this is using the startAccessory prop

IconName,
} from '../../types';

export const MAP_BANNER_ALERT_SEVERITY_ICON_NAME: Record<

@georgewrmarshall georgewrmarshall Mar 10, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Type safety for severity-to-token mappings. The Record<(typeof BannerAlertSeverity)[keyof typeof BannerAlertSeverity], IconName> pattern ensures every severity value has exactly one mapping and TypeScript validates completeness. This catches missing mappings at compile time rather than runtime, critical for maintaining cross-platform consistency.

const backgroundColor =
MAP_BANNER_ALERT_SEVERITY_BACKGROUND_COLOR[severity];
const borderColorClass = MAP_BANNER_ALERT_SEVERITY_BORDER_COLOR[severity];
const mergedClassName = twMerge('border-l-4', borderColorClass, className);

@georgewrmarshall georgewrmarshall Mar 10, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct Tailwind class merging pattern. The severity-driven border (border-l-4 + color class) must merge with any user-provided className. Using twMerge ensures conflicting border utilities are properly resolved (e.g., if consumer passes border-l-2, the component's border-l-4 takes precedence). This maintains severity styling while allowing non-conflicting customization.

@@ -0,0 +1,3 @@
export { BannerAlertSeverity } from '@metamask/design-system-shared';

@georgewrmarshall georgewrmarshall Mar 10, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Component index.ts files export const objects directly from @metamask/design-system-shared (not through src/types/index.ts). This prevents duplicate exports that cause uncovered code paths in Jest coverage reports. The pattern ensures const objects have a single export location, matching the BadgeStatus proof-of-concept for ADR-0003/0004.


| Extension Pattern | Design System Migration |
| ------------------------------------------------- | ---------------------------------------------------------- |
| `BannerAlertSeverity` from `./banner-alert.types` | `BannerAlertSeverity` from `@metamask/design-system-react` |

@georgewrmarshall georgewrmarshall Mar 10, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Migration guide provides concrete before/after examples showing the import source change from local extension types to @metamask/design-system-react package. The table explicitly shows that severity string values remain identical (info, success, warning, danger), making the migration straightforward - only the import source changes, not the runtime behavior.

expect(icon).toHaveClass(IconColor.PrimaryDefault);
});

it.each([

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Table-driven test pattern validates all four severity mappings exhaustively. Each severity gets icon color and background color assertions, ensuring the constants file mappings are correctly wired through to the rendered output. This catches any mismatch between constants and actual component behavior.


// BannerAlert types (ADR-0003 + ADR-0004)
export {
BannerAlertSeverity,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inline type keyword prevents duplicate identifier errors. Exporting both the const object (BannerAlertSeverity) and the type with the same name requires using the inline type keyword for the type export. Without it, TypeScript would see duplicate identifiers and fail compilation. This pattern is used consistently across all ADR-0003/0004 component exports.

@georgewrmarshall georgewrmarshall enabled auto-merge (squash) March 10, 2026 22:26
@github-actions

Copy link
Copy Markdown
Contributor

📖 Storybook Preview

@georgewrmarshall georgewrmarshall merged commit 4a35ece into main Mar 10, 2026
43 checks passed
@georgewrmarshall georgewrmarshall deleted the banner-alert-react branch March 10, 2026 22:29

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

Bugbot Autofix is ON, but it could not run because the branch was deleted or merged before autofix could start.

paddingLeft={2}
className={mergedClassName}
{...props}
/>

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.

Props spread overrides severity-driven backgroundColor

Medium Severity

{...props} is spread after the explicit backgroundColor and paddingLeft props on BannerBase, meaning any consumer-passed backgroundColor silently overrides the severity-driven value. This contradicts the design intent evidenced by iconProps using Omit<IconProps, 'name' | 'size' | 'color'> to prevent severity overrides. backgroundColor and paddingLeft are not destructured from incoming props, so they leak into the rest spread.

Fix in Cursor Fix in Web

georgewrmarshall added a commit that referenced this pull request Mar 10, 2026
## **Description**

Adds React Native implementation and shared types for `BannerAlert`
component, broken out from the complete cross-platform implementation.
This PR introduces:

- **Shared types** in `@metamask/design-system-shared` (ADR-0003/0004
aligned)
  - `BannerAlertSeverity` const object with derived union type
  - `BannerAlertPropsShared` interface for cross-platform props
- **React Native implementation** in
`@metamask/design-system-react-native`
  - Severity-driven icon, background, and left-border styling
  - Built on `BannerBase` with design token mappings
  - Uses array pattern for tw.style() + style prop merging
  - Full Storybook stories, unit tests, and component documentation
- **Migration documentation** updated in
`packages/design-system-react-native/MIGRATION.md`

The React implementation was separated into PR #975 for focused
platform-specific review.

## **Related issues**

Related to:
- https://consensyssoftware.atlassian.net/browse/DSYS-513 (React Native
implementation)
- #975 (React implementation - separate PR)

## **Manual testing steps**

1. Run `yarn build` from repo root and verify all workspaces build.
2. Run `yarn lint` from repo root and verify lint/format/dependency
checks pass.
3. Run `yarn test` from repo root and verify package tests pass.
4. Run React Native coverage check:
- `NODE_OPTIONS=--experimental-vm-modules ./node_modules/.bin/jest -c
packages/design-system-react-native/jest.config.js --runInBand
--watchman=false`
5. Run `yarn storybook:ios` or `yarn storybook:android` and verify
`BannerAlert` renders all severities (Info, Success, Warning, Danger)
and supports action/close props.

## **Screenshots/Recordings**

### **Before**

N/A

### **After**

N/A

Default stories and args (React Native)


https://github.com/user-attachments/assets/573bf78c-f2a8-48d0-93be-965854b26b22

Other stories (React Native)


https://github.com/user-attachments/assets/a23bd123-b315-4ae9-8cbe-27773351a0d6

Docs (React Native)


https://github.com/user-attachments/assets/b3e20382-e28e-4feb-abcf-6670d5f32f42

## **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
- [ ] 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.
@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 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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants