feat: add BannerAlert React and shared types#975
Conversation
📖 Storybook Preview |
| Warning: 'warning', | ||
| /** Danger style. */ | ||
| Danger: 'danger', | ||
| } as const; |
There was a problem hiding this comment.
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 & { |
There was a problem hiding this comment.
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'>; |
There was a problem hiding this comment.
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< |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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'; | |||
There was a problem hiding this comment.
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` | |
There was a problem hiding this comment.
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([ |
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
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.
📖 Storybook Preview |
| paddingLeft={2} | ||
| className={mergedClassName} | ||
| {...props} | ||
| /> |
There was a problem hiding this comment.
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.
## **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.
## 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 -->
## 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 -->


Description
Adds React implementation and shared types for
BannerAlertcomponent, broken out from the complete cross-platform implementation in #966. This PR introduces:@metamask/design-system-shared(ADR-0003/0004 aligned)BannerAlertSeverityconst object with derived union typeBannerAlertPropsSharedinterface for cross-platform props@metamask/design-system-reactBannerBasewith design token mappingspackages/design-system-react/MIGRATION.mdThe React Native implementation will follow in a separate PR, allowing for focused review of each platform.
Related issues
Related to:
Manual testing steps
yarn buildfrom repo root and verify all workspaces build.yarn lintfrom repo root and verify lint/format/dependency checks pass.yarn testfrom repo root and verify package tests pass.NODE_OPTIONS=--experimental-vm-modules ./node_modules/.bin/jest -c packages/design-system-react/jest.config.js --runInBand --watchman=falseyarn storybookand verifyBannerAlertrenders 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
Pre-merge reviewer checklist
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
BannerAlertReact component that composesBannerBaseand applies severity-driven icon, background, and left-border styling, exporting it (andBannerAlertSeverity) from the design system component index.Introduces shared
BannerAlertSeverityandBannerAlertPropsSharedin@metamask/design-system-shared, wires them into package exports, and adds Storybook stories, unit tests, and docs for the new component.Updates
MIGRATION.mdwith extension-to-design-system guidance forBannerAlertSeverityimports/values.Written by Cursor Bugbot for commit 43fd00c. This will update automatically on new commits. Configure here.