Skip to content

feat: add BannerAlert React Native and shared types#966

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

feat: add BannerAlert React Native and shared types#966
georgewrmarshall merged 23 commits into
mainfrom
banner-alert

Conversation

@georgewrmarshall

@georgewrmarshall georgewrmarshall commented Mar 6, 2026

Copy link
Copy Markdown
Contributor

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:

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)

banneralert.defautstory.mov

Other stories (React Native)

banner.alert.stories.mov

Docs (React Native)

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
Mostly additive UI component work (new component, exports, docs, stories, and tests) with minimal impact on existing runtime behavior beyond new public API surface.

Overview
Adds a new BannerAlert component to @metamask/design-system-react-native, built on BannerBase and driven by shared BannerAlertSeverity to map severity to icon, colors, and a left-border style.

Includes Storybook stories and unit tests for severity/default behavior and style merging, updates MIGRATION.md with BannerAlert-specific breaking changes/mapping, and exports the new component/types from the package (and registers the story in RN Storybook’s auto-generated storybook.requires.js).

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

@github-actions

github-actions Bot commented Mar 6, 2026

Copy link
Copy Markdown
Contributor

📖 Storybook Preview

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

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: React constants hardcode severity type, diverging from shared
    • Updated React constants to import BannerAlertSeverity from the shared package, derive the severity union type from it, and set the default via BannerAlertSeverity.Info, ensuring Record maps are compile-time validated against shared severities.

Create PR

Or push these changes by commenting:

@cursor push 1e100bdb90
Preview (1e100bdb90)
diff --git a/packages/design-system-react/src/components/BannerAlert/BannerAlert.constants.ts b/packages/design-system-react/src/components/BannerAlert/BannerAlert.constants.ts
--- a/packages/design-system-react/src/components/BannerAlert/BannerAlert.constants.ts
+++ b/packages/design-system-react/src/components/BannerAlert/BannerAlert.constants.ts
@@ -1,8 +1,10 @@
+import { BannerAlertSeverity } from '@metamask/design-system-shared';
 import { BoxBackgroundColor, IconColor, IconName, IconSize } from '../../types';
 
-type BannerAlertSeverityType = 'info' | 'success' | 'warning' | 'danger';
+type BannerAlertSeverityType =
+  (typeof BannerAlertSeverity)[keyof typeof BannerAlertSeverity];
 
-export const DEFAULT_BANNER_ALERT_SEVERITY: BannerAlertSeverityType = 'info';
+export const DEFAULT_BANNER_ALERT_SEVERITY = BannerAlertSeverity.Info;
 
 export const MAP_BANNER_ALERT_SEVERITY_ICON_NAME: Record<
   BannerAlertSeverityType,

Comment thread packages/design-system-react/src/components/BannerAlert/BannerAlert.constants.ts Outdated
@github-actions

github-actions Bot commented Mar 9, 2026

Copy link
Copy Markdown
Contributor

📖 Storybook Preview

@github-actions

github-actions Bot commented Mar 9, 2026

Copy link
Copy Markdown
Contributor

📖 Storybook Preview

@github-actions

github-actions Bot commented Mar 9, 2026

Copy link
Copy Markdown
Contributor

📖 Storybook Preview

@github-actions

github-actions Bot commented Mar 9, 2026

Copy link
Copy Markdown
Contributor

📖 Storybook Preview

Comment thread packages/design-system-react-native/src/components/BannerAlert/README.md Outdated
Comment thread packages/design-system-react/src/components/BannerAlert/BannerAlert.constants.ts Outdated
Comment thread packages/design-system-react/src/components/BannerAlert/BannerAlert.tsx Outdated
Comment thread packages/design-system-react/src/components/BannerAlert/BannerAlert.tsx Outdated
Comment thread packages/design-system-react/src/components/BannerAlert/README.mdx 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 1 potential issue.

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Spread order lets users override severity-driven props
    • Moved the props spread before explicit severity-driven props in both web and React Native BannerAlert components so consumer values cannot override computed startAccessory, backgroundColor, or paddingLeft.

Create PR

Or push these changes by commenting:

@cursor push 394e624fb0
Preview (394e624fb0)
diff --git a/packages/design-system-react-native/src/components/BannerAlert/BannerAlert.tsx b/packages/design-system-react-native/src/components/BannerAlert/BannerAlert.tsx
--- a/packages/design-system-react-native/src/components/BannerAlert/BannerAlert.tsx
+++ b/packages/design-system-react-native/src/components/BannerAlert/BannerAlert.tsx
@@ -38,6 +38,7 @@
 
   return (
     <BannerBase
+      {...props}
       startAccessory={
         <Icon
           testID={BANNER_ALERT_ICON_TEST_ID}
@@ -49,7 +50,6 @@
       backgroundColor={backgroundColor}
       paddingLeft={2}
       twClassName={mergedTwClassName}
-      {...props}
     />
   );
 };

diff --git a/packages/design-system-react/src/components/BannerAlert/BannerAlert.tsx b/packages/design-system-react/src/components/BannerAlert/BannerAlert.tsx
--- a/packages/design-system-react/src/components/BannerAlert/BannerAlert.tsx
+++ b/packages/design-system-react/src/components/BannerAlert/BannerAlert.tsx
@@ -35,6 +35,7 @@
     return (
       <BannerBase
         ref={ref}
+        {...props}
         startAccessory={
           <Icon
             data-testid={BANNER_ALERT_ICON_TEST_ID}
@@ -46,7 +47,6 @@
         backgroundColor={backgroundColor}
         paddingLeft={2}
         className={mergedClassName}
-        {...props}
       />
     );
   },

@github-actions

github-actions Bot commented Mar 9, 2026

Copy link
Copy Markdown
Contributor

📖 Storybook Preview

@github-actions

github-actions Bot commented Mar 9, 2026

Copy link
Copy Markdown
Contributor

📖 Storybook Preview

@github-actions

github-actions Bot commented Mar 9, 2026

Copy link
Copy Markdown
Contributor

📖 Storybook Preview

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

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Unnecessary variable alias adds dead indirection
    • Removed the redundant resolvedSeverity alias and used severity directly in all map lookups.

Create PR

Or push these changes by commenting:

@cursor push 8d09acce91
Preview (8d09acce91)
diff --git a/packages/design-system-react-native/src/components/BannerAlert/BannerAlert.tsx b/packages/design-system-react-native/src/components/BannerAlert/BannerAlert.tsx
--- a/packages/design-system-react-native/src/components/BannerAlert/BannerAlert.tsx
+++ b/packages/design-system-react-native/src/components/BannerAlert/BannerAlert.tsx
@@ -19,13 +19,12 @@
   twClassName,
   ...props
 }) => {
-  const resolvedSeverity = severity;
-  const iconName = MAP_BANNER_ALERT_SEVERITY_ICON_NAME[resolvedSeverity];
-  const iconColor = MAP_BANNER_ALERT_SEVERITY_ICON_COLOR[resolvedSeverity];
+  const iconName = MAP_BANNER_ALERT_SEVERITY_ICON_NAME[severity];
+  const iconColor = MAP_BANNER_ALERT_SEVERITY_ICON_COLOR[severity];
   const backgroundColor =
-    MAP_BANNER_ALERT_SEVERITY_BACKGROUND_COLOR[resolvedSeverity];
+    MAP_BANNER_ALERT_SEVERITY_BACKGROUND_COLOR[severity];
   const borderColorClass =
-    MAP_BANNER_ALERT_SEVERITY_BORDER_COLOR[resolvedSeverity] ??
+    MAP_BANNER_ALERT_SEVERITY_BORDER_COLOR[severity] ??
     BoxBorderColor.BorderDefault;
   const mergedTwClassName = `border-l-4 ${borderColorClass}${
     twClassName ? ` ${twClassName}` : ''

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

github-actions Bot commented Mar 9, 2026

Copy link
Copy Markdown
Contributor

📖 Storybook Preview

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

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Inconsistent iconProps spread order across platforms
    • Updated React BannerAlert to spread iconProps before severity-driven props to match React Native and ensure consistent runtime behavior.

Create PR

Or push these changes by commenting:

@cursor push a4794ee24b
Preview (a4794ee24b)
diff --git a/packages/design-system-react/src/components/BannerAlert/BannerAlert.tsx b/packages/design-system-react/src/components/BannerAlert/BannerAlert.tsx
--- a/packages/design-system-react/src/components/BannerAlert/BannerAlert.tsx
+++ b/packages/design-system-react/src/components/BannerAlert/BannerAlert.tsx
@@ -33,10 +33,10 @@
         ref={ref}
         startAccessory={
           <Icon
+            {...iconProps}
             name={iconName}
             color={iconColor}
             size={IconSize.Lg}
-            {...iconProps}
           />
         }
         backgroundColor={backgroundColor}

@github-actions

github-actions Bot commented Mar 9, 2026

Copy link
Copy Markdown
Contributor

📖 Storybook Preview

@github-actions

github-actions Bot commented Mar 9, 2026

Copy link
Copy Markdown
Contributor

📖 Storybook Preview

@github-actions

github-actions Bot commented Mar 9, 2026

Copy link
Copy Markdown
Contributor

📖 Storybook Preview

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

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Test asserts non-existent data-name attribute on Icon
    • Updated the web BannerAlert test to mock the Icon component so it renders a data-name attribute, allowing the assertions to correctly verify the rendered icon without changing production code.

Create PR

Or push these changes by commenting:

@cursor push df4c553835
Preview (df4c553835)
diff --git a/packages/design-system-react/src/components/BannerAlert/BannerAlert.test.tsx b/packages/design-system-react/src/components/BannerAlert/BannerAlert.test.tsx
--- a/packages/design-system-react/src/components/BannerAlert/BannerAlert.test.tsx
+++ b/packages/design-system-react/src/components/BannerAlert/BannerAlert.test.tsx
@@ -7,6 +7,18 @@
 
 import { BannerAlertSeverity } from '.';
 
+jest.mock('../Icon', () => {
+  const React = require('react');
+  return {
+    Icon: ({ name, color, ...rest }: any) =>
+      React.createElement('svg', {
+        'data-name': name,
+        'data-testid': (rest as any)['data-testid'],
+        className: color,
+      }),
+  };
+});
+
 const ICON_TEST_ID = 'banner-alert-icon';
 
 describe('BannerAlert', () => {

Comment thread packages/design-system-react/src/components/BannerAlert/BannerAlert.test.tsx Outdated
@georgewrmarshall georgewrmarshall changed the title feat: migrate BannerAlert from extension and mobile to MMDS feat: add BannerAlert React Native and shared types Mar 10, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📖 Storybook Preview

@georgewrmarshall

Copy link
Copy Markdown
Contributor Author

Updated PR to focus on React Native + shared types implementation only.

React Native implementation (design-system-react-native)
Shared types (design-system-shared)
Migration documentation for React Native

The React implementation has been moved to PR #975 for separate, focused review of each platform.

Key patterns demonstrated:

  • ADR-0003/0004 compliance (const objects + centralized types)
  • Array pattern for tw.style() + style prop merging
  • Built on BannerBase with severity-driven token mappings

@github-actions

Copy link
Copy Markdown
Contributor

📖 Storybook Preview

}
backgroundColor={backgroundColor}
paddingLeft={2}
style={[tw.style(`border-l-4 ${borderColorClass}`), style]}

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

Array pattern for tw.style() + style prop merging. This should be the new recommended React Native pattern from the styling guidelines. Created PR for rule update here #974

| `BannerAlertSeverity.Info` (`'Info'`) | `BannerAlertSeverity.Info` (`'info'`) | casing changed |
| `BannerAlertSeverity.Success` (`'Success'`) | `BannerAlertSeverity.Success` (`'success'`) | casing changed |
| `BannerAlertSeverity.Warning` (`'Warning'`) | `BannerAlertSeverity.Warning` (`'warning'`) | casing changed |
| `BannerAlertSeverity.Error` (`'Error'`) | `BannerAlertSeverity.Danger` (`'danger'`) | renamed |

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.

Breaking change: Error → Danger rename. Mobile's BannerAlertSeverity.Error is renamed to BannerAlertSeverity.Danger for consistency with the cross-platform design system. The migration guide documents this rename explicitly with before/after examples. Additionally, all severity values changed from PascalCase string values ('Info', 'Success') to lowercase ('info', 'success') for consistency with web platform and design tokens.


describe('BannerAlert', () => {
const mockBannerBase = BannerBase as jest.Mock;
const mockTwStyle = jest.fn((...args) => args.filter(Boolean));

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.

React Native test pattern for tw.style mocking. The mockTwStyle function returns filtered args to simulate tw.style behavior without actual Tailwind processing. This allows tests to verify the correct classes are passed to tw.style (line 93-95) and that the array pattern is used correctly (line 98-100). The mock filters out falsy values with .filter(Boolean), matching real tw.style behavior for conditional classes.

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.

Cross-platform constants consistency. These MAP constants are identical across React and React Native implementations (same key names, same enum values). We could consider moving these consts into shared if it makes sense in a subsequent PR

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

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 for icon. The component internally controls icon name, size, and color based on severity mappings, but consumers may need to attach test IDs or platform-specific styling. Omitting the controlled props (name, size, color) prevents accidental overrides that would break severity semantics while allowing testID, accessibilityLabel, and other React Native-specific Icon props.

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

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.

Direct export from shared package. 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.

expect(props.startAccessory.props.testID).toBe(ICON_TEST_ID);
});

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 validates all severity mappings. Each severity (Info, Success, Warning, Danger) gets exhaustive assertions for icon name, icon color, and background color. This ensures the constants file mappings correctly wire through to the rendered component. The test catches any mismatch between MAP constants and actual component behavior, and validates cross-platform consistency (same severity values and mappings as React Web).

* BannerAlert severity variants.
* Uses const object with derived union type (ADR-0003).
*/
export const 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.

Const object with derived union type (ADR-0003). This replaces TypeScript enums with runtime const objects that have derived union types. Benefits include better tree-shaking, simpler cross-package imports without enum re-export issues, and consistency with the shared package architecture. The lowercase values (info, success, warning, danger) align with React Web and design token naming conventions.

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

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.

Extends BannerBasePropsShared (ADR-0004 centralized types). Shared props inherit from BannerBase to get all banner functionality (title, description, action buttons, close button) while adding only the severity prop. This creates a clear component hierarchy where BannerBase provides structure and BannerAlert adds semantic severity styling. The pattern ensures consistent props across React and React Native platforms.


// 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. When exporting both a const object and a type with the same name (BannerAlertSeverity), the inline type keyword is required for the type export. Without it, TypeScript sees duplicate identifiers and compilation fails. This pattern is used consistently across all ADR-0003/0004 component exports in the shared package.

@georgewrmarshall georgewrmarshall marked this pull request as ready for review March 10, 2026 21:01
@georgewrmarshall georgewrmarshall requested a review from a team as a code owner March 10, 2026 21:01
georgewrmarshall added a commit that referenced this pull request Mar 10, 2026
## **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:
- https://consensyssoftware.atlassian.net/browse/DSYS-512 (React
implementation)
- #966 (Original complete cross-platform 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 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)


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

Other stories (React Web)


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

Docs (React Web)


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.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Primarily additive UI/component work with isolated exports and
coverage via stories/tests; low likelihood of impacting existing
behavior aside from any downstream import/export expectations.
> 
> **Overview**
> Adds a new `BannerAlert` React component (built on `BannerBase`) that
applies severity-driven icon, background, and left-border styling via
shared `BannerAlertSeverity` and token mapping constants.
> 
> Introduces shared cross-platform `BannerAlertSeverity` and
`BannerAlertPropsShared` exports in `@metamask/design-system-shared`,
wires `BannerAlert`/types into the React package public exports, and
adds Storybook stories, unit tests, component docs (`README.mdx`), plus
migration guide updates covering severity import/source changes.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
990b04d. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
@georgewrmarshall georgewrmarshall enabled auto-merge (squash) March 10, 2026 22:32
@github-actions

Copy link
Copy Markdown
Contributor

📖 Storybook Preview

@georgewrmarshall georgewrmarshall merged commit 9350e5c into main Mar 10, 2026
43 checks passed
@georgewrmarshall georgewrmarshall deleted the banner-alert branch March 10, 2026 22:35
georgewrmarshall added a commit that referenced this pull request Mar 23, 2026
## **Description**

Extracts and documents the React Native `tw.style()` array pattern from
the BannerAlert migration (PR #966) into the styling cursor rule. This
pattern is essential for components that need to apply Tailwind classes
while accepting a custom `style` prop.

**What is the reason for the change?**
- The BannerAlert component migration introduced an important pattern
for merging `tw.style()` with style props using arrays
- This pattern ensures type safety and proper merge order (tw classes →
twClassName → style prop)
- The pattern needs to be documented in the styling rule so it's
available as a reference for all future components

**What is the improvement/solution?**
- Adds comprehensive documentation of the array pattern to
`.cursor/rules/styling.md`
- Includes correct ✅ and incorrect ❌ examples
- Explains why the array pattern is preferred (type safety, merge order,
consistency with Box)
- Provides real-world example from BannerAlert component
- Ensures AI agents and developers follow this pattern consistently

This documentation was originally added as part of PR #966 but is being
extracted into a standalone documentation update for better visibility
and reusability.

## **Related issues**

Related to:
- #966
(BannerAlert migration)

## **Manual testing steps**

1. Review the updated `.cursor/rules/styling.md` file
2. Verify the array pattern documentation is clear and comprehensive
3. Confirm examples match the BannerAlert implementation

## **Screenshots/Recordings**

N/A - Documentation only change

### **Before**

No documentation for merging `tw.style()` with style props

### **After**

Comprehensive documentation including:
- ❌ Wrong pattern (passing style directly to tw.style)
- ✅ Correct pattern (array with tw.style and style)
- Why array pattern explanation
- Real-world BannerAlert example

## **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 (N/A - documentation only)
- [x] I've documented my code using [JSDoc](https://jsdoc.app/) format
if applicable (N/A)
- [ ] 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]
> **Low Risk**
> Low risk because this PR only updates documentation and adds no
runtime code changes.
> 
> **Overview**
> Documents a React Native **array-based merge pattern** for combining
component `tw.style()` classes with a passed-in `style` prop, while
keeping `twClassName` forwarded separately.
> 
> Adds do/don’t examples, rationale (type-safety and consistent merge
behavior), and a `BannerAlert` snippet illustrating the recommended
approach.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
de51983. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
@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
## **Description**

Extracts and documents the React Native `tw.style()` array pattern from
the BannerAlert migration (PR #966) into the styling cursor rule. This
pattern is essential for components that need to apply Tailwind classes
while accepting a custom `style` prop.

**What is the reason for the change?**
- The BannerAlert component migration introduced an important pattern
for merging `tw.style()` with style props using arrays
- This pattern ensures type safety and proper merge order (tw classes →
twClassName → style prop)
- The pattern needs to be documented in the styling rule so it's
available as a reference for all future components

**What is the improvement/solution?**
- Adds comprehensive documentation of the array pattern to
`.cursor/rules/styling.md`
- Includes correct ✅ and incorrect ❌ examples
- Explains why the array pattern is preferred (type safety, merge order,
consistency with Box)
- Provides real-world example from BannerAlert component
- Ensures AI agents and developers follow this pattern consistently

This documentation was originally added as part of PR #966 but is being
extracted into a standalone documentation update for better visibility
and reusability.

## **Related issues**

Related to:
- #966
(BannerAlert migration)

## **Manual testing steps**

1. Review the updated `.cursor/rules/styling.md` file
2. Verify the array pattern documentation is clear and comprehensive
3. Confirm examples match the BannerAlert implementation

## **Screenshots/Recordings**

N/A - Documentation only change

### **Before**

No documentation for merging `tw.style()` with style props

### **After**

Comprehensive documentation including:
- ❌ Wrong pattern (passing style directly to tw.style)
- ✅ Correct pattern (array with tw.style and style)
- Why array pattern explanation
- Real-world BannerAlert example

## **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 (N/A - documentation only)
- [x] I've documented my code using [JSDoc](https://jsdoc.app/) format
if applicable (N/A)
- [ ] 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]
> **Low Risk**
> Low risk because this PR only updates documentation and adds no
runtime code changes.
> 
> **Overview**
> Documents a React Native **array-based merge pattern** for combining
component `tw.style()` classes with a passed-in `style` prop, while
keeping `twClassName` forwarded separately.
> 
> Adds do/don’t examples, rationale (type-safety and consistent merge
behavior), and a `BannerAlert` snippet illustrating the recommended
approach.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
de51983. 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