feat: migrate BannerBase from mobile into MMDS#955
Conversation
📖 Storybook Preview |
📖 Storybook Preview |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 3 potential issues.
Autofix Details
Bugbot Autofix prepared fixes for all 3 issues found in the latest run.
- ✅ Fixed: RN close button missing accessibility label
- Added DEFAULT_BANNERBASE_CLOSE_BUTTON_ARIA_LABEL and passed it via accessibilityLabel to the RN close ButtonIcon with a default of "Close banner".
- ✅ Fixed: Close button spread overrides structural layout classes
- Destructured and merged className/twClassName so structural classes (ml-3/self-start) are preserved and user classes are merged instead of overwritten on both web and RN.
- ✅ Fixed: BannerBase exports placed before Badge breaking alphabetical order*
- Moved BannerBase exports after BadgeWrapper in both packages’ index files to restore alphabetical ordering and grouping.
Or push these changes by commenting:
@cursor push 8d0969d66e
Preview (8d0969d66e)
diff --git a/packages/design-system-react-native/src/components/BannerBase/BannerBase.constants.ts b/packages/design-system-react-native/src/components/BannerBase/BannerBase.constants.ts
--- a/packages/design-system-react-native/src/components/BannerBase/BannerBase.constants.ts
+++ b/packages/design-system-react-native/src/components/BannerBase/BannerBase.constants.ts
@@ -5,5 +5,6 @@
export const DEFAULT_BANNERBASE_ACTION_BUTTON_SIZE = ButtonSize.Md;
export const DEFAULT_BANNERBASE_CLOSE_BUTTON_SIZE = ButtonIconSize.Sm;
export const DEFAULT_BANNERBASE_CLOSE_BUTTON_ICON_NAME = IconName.Close;
+export const DEFAULT_BANNERBASE_CLOSE_BUTTON_ARIA_LABEL = 'Close banner';
export const TESTID_BANNERBASE_CLOSE_BUTTON = 'banner-base-close-button';
diff --git a/packages/design-system-react-native/src/components/BannerBase/BannerBase.tsx b/packages/design-system-react-native/src/components/BannerBase/BannerBase.tsx
--- a/packages/design-system-react-native/src/components/BannerBase/BannerBase.tsx
+++ b/packages/design-system-react-native/src/components/BannerBase/BannerBase.tsx
@@ -14,6 +14,7 @@
import {
DEFAULT_BANNERBASE_ACTION_BUTTON_SIZE,
+ DEFAULT_BANNERBASE_CLOSE_BUTTON_ARIA_LABEL,
DEFAULT_BANNERBASE_CLOSE_BUTTON_ICON_NAME,
DEFAULT_BANNERBASE_CLOSE_BUTTON_SIZE,
DEFAULT_BANNERBASE_DESCRIPTION_VARIANT,
@@ -48,8 +49,13 @@
...resolvedActionButtonProps
} = actionButtonProps ?? {};
- const { onPress: closeButtonPropsOnPress, ...resolvedCloseButtonProps } =
- closeButtonProps ?? {};
+ const {
+ accessibilityLabel:
+ closeButtonAccessibilityLabel = DEFAULT_BANNERBASE_CLOSE_BUTTON_ARIA_LABEL,
+ onPress: closeButtonPropsOnPress,
+ twClassName: closeButtonTwClassName,
+ ...resolvedCloseButtonProps
+ } = closeButtonProps ?? {};
const resolvedActionButtonLabel =
actionButtonLabel ??
@@ -71,6 +77,10 @@
}
: undefined;
+ const mergedCloseButtonTwClassName = closeButtonTwClassName
+ ? `ml-3 ${closeButtonTwClassName}`
+ : 'ml-3';
+
return (
<Box
flexDirection={BoxFlexDirection.Row}
@@ -128,9 +138,10 @@
{shouldShowCloseButton && (
<ButtonIcon
testID={TESTID_BANNERBASE_CLOSE_BUTTON}
- twClassName="ml-3"
+ twClassName={mergedCloseButtonTwClassName}
iconName={DEFAULT_BANNERBASE_CLOSE_BUTTON_ICON_NAME}
size={DEFAULT_BANNERBASE_CLOSE_BUTTON_SIZE}
+ accessibilityLabel={closeButtonAccessibilityLabel}
onPress={handleClosePress}
{...resolvedCloseButtonProps}
/>
diff --git a/packages/design-system-react-native/src/components/index.ts b/packages/design-system-react-native/src/components/index.ts
--- a/packages/design-system-react-native/src/components/index.ts
+++ b/packages/design-system-react-native/src/components/index.ts
@@ -30,9 +30,6 @@
export { AvatarToken, AvatarTokenSize } from './AvatarToken';
export type { AvatarTokenProps } from './AvatarToken';
-export { BannerBase } from './BannerBase';
-export type { BannerBaseProps } from './BannerBase';
-
export { BadgeCount, BadgeCountSize } from './BadgeCount';
export type { BadgeCountProps } from './BadgeCount';
@@ -55,6 +52,9 @@
BadgeWrapperCustomPosition,
} from './BadgeWrapper';
+export { BannerBase } from './BannerBase';
+export type { BannerBaseProps } from './BannerBase';
+
export { BottomSheetFooter, ButtonsAlignment } from './BottomSheetFooter';
export type {
BottomSheetFooterProps,
diff --git a/packages/design-system-react/src/components/BannerBase/BannerBase.tsx b/packages/design-system-react/src/components/BannerBase/BannerBase.tsx
--- a/packages/design-system-react/src/components/BannerBase/BannerBase.tsx
+++ b/packages/design-system-react/src/components/BannerBase/BannerBase.tsx
@@ -57,6 +57,7 @@
ariaLabel:
closeButtonAriaLabel = DEFAULT_BANNERBASE_CLOSE_BUTTON_ARIA_LABEL,
onClick: closeButtonPropsOnClick,
+ className: closeButtonClassName,
...resolvedCloseButtonProps
} = closeButtonProps ?? {};
@@ -127,7 +128,7 @@
{shouldShowCloseButton && (
<ButtonIcon
data-testid={TESTID_BANNERBASE_CLOSE_BUTTON}
- className="ml-3 self-start"
+ className={twMerge('ml-3 self-start', closeButtonClassName)}
iconName={DEFAULT_BANNERBASE_CLOSE_BUTTON_ICON_NAME}
size={DEFAULT_BANNERBASE_CLOSE_BUTTON_SIZE}
ariaLabel={closeButtonAriaLabel}
diff --git a/packages/design-system-react/src/components/index.ts b/packages/design-system-react/src/components/index.ts
--- a/packages/design-system-react/src/components/index.ts
+++ b/packages/design-system-react/src/components/index.ts
@@ -27,9 +27,6 @@
export { AvatarToken, AvatarTokenSize } from './AvatarToken';
export type { AvatarTokenProps } from './AvatarToken';
-export { BannerBase } from './BannerBase';
-export type { BannerBaseProps } from './BannerBase';
-
export { BadgeCount } from './BadgeCount';
export type { BadgeCountProps } from './BadgeCount';
export { BadgeCountSize } from './BadgeCount';
@@ -52,6 +49,9 @@
} from './BadgeWrapper';
export type { BadgeWrapperCustomPosition } from './BadgeWrapper';
+export { BannerBase } from './BannerBase';
+export type { BannerBaseProps } from './BannerBase';
+
export { Blockies } from './temp-components/Blockies';
export type { BlockiesProps } from './temp-components/Blockies';
📖 Storybook Preview |
There was a problem hiding this comment.
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: Falsy numeric values bypass Text wrapping in RN
- Replaced truthy guards with explicit null checks so 0 is wrapped in Text for title, description, and children in both RN and Web components.
Or push these changes by commenting:
@cursor push de46f72021
Preview (de46f72021)
diff --git a/packages/design-system-react-native/src/components/BannerBase/BannerBase.tsx b/packages/design-system-react-native/src/components/BannerBase/BannerBase.tsx
--- a/packages/design-system-react-native/src/components/BannerBase/BannerBase.tsx
+++ b/packages/design-system-react-native/src/components/BannerBase/BannerBase.tsx
@@ -69,7 +69,8 @@
{startAccessory}
<Box twClassName="flex-1">
- {title &&
+ {title !== null &&
+ title !== undefined &&
(isTextContent(title) ? (
<Text
variant={TextVariant.BodyMd}
@@ -82,7 +83,7 @@
title
))}
- {description && (
+ {description !== null && description !== undefined && (
<Box twClassName={title ? 'mt-1' : undefined}>
{isTextContent(description) ? (
<Text variant={TextVariant.BodySm} {...descriptionProps}>
@@ -94,7 +95,8 @@
</Box>
)}
- {children &&
+ {children !== null &&
+ children !== undefined &&
(isTextContent(children) ? (
<Text variant={TextVariant.BodyMd} {...childrenWrapperProps}>
{children}
diff --git a/packages/design-system-react/src/components/BannerBase/BannerBase.tsx b/packages/design-system-react/src/components/BannerBase/BannerBase.tsx
--- a/packages/design-system-react/src/components/BannerBase/BannerBase.tsx
+++ b/packages/design-system-react/src/components/BannerBase/BannerBase.tsx
@@ -66,7 +66,8 @@
{startAccessory}
<Box className="min-w-0 flex-1">
- {title &&
+ {title !== null &&
+ title !== undefined &&
(isTextContent(title) ? (
<Text
variant={TextVariant.BodyMd}
@@ -79,7 +80,8 @@
title
))}
- {description && (
+ {description !== null &&
+ description !== undefined && (
<Box className={title ? 'mt-1' : undefined}>
{isTextContent(description) ? (
<Text variant={TextVariant.BodySm} {...descriptionProps}>
@@ -91,7 +93,8 @@
</Box>
)}
- {children &&
+ {children !== null &&
+ children !== undefined &&
(isTextContent(children) ? (
<Text variant={TextVariant.BodyMd} {...childrenWrapperProps}>
{children}
📖 Storybook Preview |
📖 Storybook Preview |
📖 Storybook Preview |
There was a problem hiding this comment.
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: BannerBase exports break alphabetical ordering convention
- Moved the BannerBase export to follow all Badge* exports in both React and React Native component barrel files to restore alphabetical order.
Or push these changes by commenting:
@cursor push e46c799a0f
Preview (e46c799a0f)
diff --git a/packages/design-system-react-native/src/components/index.ts b/packages/design-system-react-native/src/components/index.ts
--- a/packages/design-system-react-native/src/components/index.ts
+++ b/packages/design-system-react-native/src/components/index.ts
@@ -30,9 +30,6 @@
export { AvatarToken, AvatarTokenSize } from './AvatarToken';
export type { AvatarTokenProps } from './AvatarToken';
-export { BannerBase } from './BannerBase';
-export type { BannerBaseProps } from './BannerBase';
-
export { BadgeCount, BadgeCountSize } from './BadgeCount';
export type { BadgeCountProps } from './BadgeCount';
@@ -55,6 +52,9 @@
BadgeWrapperCustomPosition,
} from './BadgeWrapper';
+export { BannerBase } from './BannerBase';
+export type { BannerBaseProps } from './BannerBase';
+
export { BottomSheetFooter, ButtonsAlignment } from './BottomSheetFooter';
export type {
BottomSheetFooterProps,
diff --git a/packages/design-system-react/src/components/index.ts b/packages/design-system-react/src/components/index.ts
--- a/packages/design-system-react/src/components/index.ts
+++ b/packages/design-system-react/src/components/index.ts
@@ -27,9 +27,6 @@
export { AvatarToken, AvatarTokenSize } from './AvatarToken';
export type { AvatarTokenProps } from './AvatarToken';
-export { BannerBase } from './BannerBase';
-export type { BannerBaseProps } from './BannerBase';
-
export { BadgeCount } from './BadgeCount';
export type { BadgeCountProps } from './BadgeCount';
export { BadgeCountSize } from './BadgeCount';
@@ -52,6 +49,9 @@
} from './BadgeWrapper';
export type { BadgeWrapperCustomPosition } from './BadgeWrapper';
+export { BannerBase } from './BannerBase';
+export type { BannerBaseProps } from './BannerBase';
+
export { Blockies } from './temp-components/Blockies';
export type { BlockiesProps } from './temp-components/Blockies';
📖 Storybook Preview |
📖 Storybook Preview |
There was a problem hiding this comment.
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: Falsy numeric title breaks description margin spacing
- Replaced the truthiness check with a nullish check so a numeric title of 0 still applies the mt-1 spacing for the description in both React and React Native components.
Or push these changes by commenting:
@cursor push 75330899a4
Preview (75330899a4)
diff --git a/packages/design-system-react-native/src/components/BannerBase/BannerBase.tsx b/packages/design-system-react-native/src/components/BannerBase/BannerBase.tsx
--- a/packages/design-system-react-native/src/components/BannerBase/BannerBase.tsx
+++ b/packages/design-system-react-native/src/components/BannerBase/BannerBase.tsx
@@ -92,7 +92,11 @@
))}
{description !== null && description !== undefined && (
- <Box twClassName={title ? 'mt-1' : undefined}>
+ <Box
+ twClassName={
+ title !== null && title !== undefined ? 'mt-1' : undefined
+ }
+ >
{isTextContent(description) ? (
<Text variant={TextVariant.BodySm} {...descriptionProps}>
{description}
diff --git a/packages/design-system-react/src/components/BannerBase/BannerBase.tsx b/packages/design-system-react/src/components/BannerBase/BannerBase.tsx
--- a/packages/design-system-react/src/components/BannerBase/BannerBase.tsx
+++ b/packages/design-system-react/src/components/BannerBase/BannerBase.tsx
@@ -82,7 +82,11 @@
))}
{description !== null && description !== undefined && (
- <Box className={title ? 'mt-1' : undefined}>
+ <Box
+ className={
+ title !== null && title !== undefined ? 'mt-1' : undefined
+ }
+ >
{isTextContent(description) ? (
<Text variant={TextVariant.BodySm} {...descriptionProps}>
{description}
📖 Storybook Preview |
📖 Storybook Preview |
There was a problem hiding this comment.
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:
hasContentmishandles booleanReactNodevalues causing incorrect spacing- Updated hasContent to treat boolean values as no content in both BannerBase implementations, preventing false titles from triggering spacing.
Or push these changes by commenting:
@cursor push 777bf4a8a2
Preview (777bf4a8a2)
diff --git a/packages/design-system-react-native/src/components/BannerBase/BannerBase.tsx b/packages/design-system-react-native/src/components/BannerBase/BannerBase.tsx
--- a/packages/design-system-react-native/src/components/BannerBase/BannerBase.tsx
+++ b/packages/design-system-react-native/src/components/BannerBase/BannerBase.tsx
@@ -22,7 +22,7 @@
typeof content === 'string' || typeof content === 'number';
const hasContent = (content: React.ReactNode) =>
- content !== null && content !== undefined;
+ content !== null && content !== undefined && typeof content !== 'boolean';
export const BannerBase: React.FC<BannerBaseProps> = ({
title,
diff --git a/packages/design-system-react/src/components/BannerBase/BannerBase.tsx b/packages/design-system-react/src/components/BannerBase/BannerBase.tsx
--- a/packages/design-system-react/src/components/BannerBase/BannerBase.tsx
+++ b/packages/design-system-react/src/components/BannerBase/BannerBase.tsx
@@ -22,7 +22,7 @@
typeof content === 'string' || typeof content === 'number';
const hasContent = (content: React.ReactNode) =>
- content !== null && content !== undefined;
+ content !== null && content !== undefined && typeof content !== 'boolean';
export const BannerBase = forwardRef<HTMLDivElement, BannerBaseProps>(
(| typeof content === 'string' || typeof content === 'number'; | ||
|
|
||
| const hasContent = (content: React.ReactNode) => | ||
| content !== null && content !== undefined; |
There was a problem hiding this comment.
hasContent mishandles boolean ReactNode values causing incorrect spacing
Low Severity
The hasContent helper only excludes null and undefined, so false (and true) pass the check. React renders booleans as nothing, but the component treats them as present content. This matters for the common title={condition && "Title"} pattern — when condition is falsy, title becomes false, the title section renders an invisible node, and the description incorrectly receives mt-1 spacing as if a title is visible above it.
Additional Locations (1)
|
@metamaskbot publish-preview |
|
Preview builds have been published. See these instructions for more information about preview builds. Expand for full list of packages and versions. |
georgewrmarshall
left a comment
There was a problem hiding this comment.
Left a suggestion regarding close button test id
|
|
||
| {shouldShowCloseButton && ( | ||
| <ButtonIcon | ||
| testID="banner-base-close-button" |
There was a problem hiding this comment.
Should not have a default close button here
|
|
||
| {shouldShowCloseButton && ( | ||
| <ButtonIcon | ||
| data-testid="banner-base-close-button" |
There was a problem hiding this comment.
Should not have a default test id here should be applied using closeButtonProps update component and tests and code example in both react and react native docs
694a8ed to
15e61b1
Compare
📖 Storybook Preview |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
Autofix Details
Bugbot Autofix prepared fixes for both issues found in the latest run.
- ✅ Fixed: Duplicate utility functions across platform packages
- Moved isTextContent/hasContent to @metamask/design-system-shared and updated both platform BannerBase components to import them.
- ✅ Fixed: Close button missing
self-startin React Native- Added 'self-start' to the React Native close ButtonIcon twClassName to match web alignment behavior.
Or push these changes by commenting:
@cursor push 4f2bc1c17e
Preview (4f2bc1c17e)
diff --git a/packages/design-system-react-native/src/components/BannerBase/BannerBase.tsx b/packages/design-system-react-native/src/components/BannerBase/BannerBase.tsx
--- a/packages/design-system-react-native/src/components/BannerBase/BannerBase.tsx
+++ b/packages/design-system-react-native/src/components/BannerBase/BannerBase.tsx
@@ -1,3 +1,4 @@
+import { hasContent, isTextContent } from '@metamask/design-system-shared';
import React from 'react';
import { GestureResponderEvent } from 'react-native';
@@ -18,12 +19,6 @@
import type { BannerBaseProps } from './BannerBase.types';
-const isTextContent = (content: React.ReactNode): content is string | number =>
- typeof content === 'string' || typeof content === 'number';
-
-const hasContent = (content: React.ReactNode) =>
- content !== null && content !== undefined;
-
export const BannerBase: React.FC<BannerBaseProps> = ({
title,
titleProps,
@@ -64,8 +59,8 @@
: undefined;
const mergedCloseButtonTwClassName = closeButtonTwClassName
- ? `ml-3 ${closeButtonTwClassName}`
- : 'ml-3';
+ ? `ml-3 self-start ${closeButtonTwClassName}`
+ : 'ml-3 self-start';
return (
<Box
diff --git a/packages/design-system-react/src/components/BannerBase/BannerBase.tsx b/packages/design-system-react/src/components/BannerBase/BannerBase.tsx
--- a/packages/design-system-react/src/components/BannerBase/BannerBase.tsx
+++ b/packages/design-system-react/src/components/BannerBase/BannerBase.tsx
@@ -1,3 +1,4 @@
+import { hasContent, isTextContent } from '@metamask/design-system-shared';
import React, { forwardRef } from 'react';
import {
@@ -18,12 +19,6 @@
import type { BannerBaseProps } from './BannerBase.types';
-const isTextContent = (content: React.ReactNode): content is string | number =>
- typeof content === 'string' || typeof content === 'number';
-
-const hasContent = (content: React.ReactNode) =>
- content !== null && content !== undefined;
-
export const BannerBase = forwardRef<HTMLDivElement, BannerBaseProps>(
(
{
diff --git a/packages/design-system-shared/src/index.ts b/packages/design-system-shared/src/index.ts
--- a/packages/design-system-shared/src/index.ts
+++ b/packages/design-system-shared/src/index.ts
@@ -6,6 +6,9 @@
generateIconSeed,
} from './utils/caip-address';
+// BannerBase utils (ADR-0004)
+export { isTextContent, hasContent } from './utils/banner-base';
+
// BadgeCount types (ADR-0003 + ADR-0004)
export { BadgeCountSize, type BadgeCountPropsShared } from './types/BadgeCount';
diff --git a/packages/design-system-shared/src/utils/banner-base.ts b/packages/design-system-shared/src/utils/banner-base.ts
new file mode 100644
--- /dev/null
+++ b/packages/design-system-shared/src/utils/banner-base.ts
@@ -1,0 +1,27 @@
+import type { ReactNode } from 'react';
+
+/**
+ * Returns true when the provided ReactNode is plain text content that should be
+ * wrapped with a Text component for consistent typography.
+ */
+/**
+ * @param content - The ReactNode to test.
+ * @returns True if content is a string or number (textual), false otherwise.
+ */
+export const isTextContent = (
+ content: ReactNode,
+): content is string | number => {
+ return typeof content === 'string' || typeof content === 'number';
+};
+
+/**
+ * Returns true when the provided ReactNode is present (not null/undefined).
+ * This intentionally allows values like 0 or empty string, which are valid content.
+ */
+/**
+ * @param content - The ReactNode to test for presence.
+ * @returns True if content is neither null nor undefined.
+ */
+export const hasContent = (content: ReactNode): boolean => {
+ return content !== null && content !== undefined;
+};15e61b1 to
f097f07
Compare
📖 Storybook Preview |
There was a problem hiding this comment.
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: HTML
titleattribute narrows sharedtitleprop type- Updated BannerBase props to omit native HTML
titlefrom BoxProps, preserving sharedtitle?: ReactNode.
- Updated BannerBase props to omit native HTML
Or push these changes by commenting:
@cursor push c32b4393c5
Preview (c32b4393c5)
diff --git a/packages/design-system-react/src/components/BannerBase/BannerBase.types.ts b/packages/design-system-react/src/components/BannerBase/BannerBase.types.ts
--- a/packages/design-system-react/src/components/BannerBase/BannerBase.types.ts
+++ b/packages/design-system-react/src/components/BannerBase/BannerBase.types.ts
@@ -26,7 +26,7 @@
};
type BannerBasePropsBase = BannerBasePropsShared &
- Omit<BoxProps, 'children'> & {
+ Omit<BoxProps, 'children' | 'title'> & {
/**
* Optional props for the title `Text` when the title is a string.
*/f097f07 to
f2283c3
Compare
📖 Storybook Preview |
| ))} | ||
|
|
||
| {shouldShowActionButton && ( | ||
| <Box twClassName="mt-4"> |
There was a problem hiding this comment.
Probably could remove some nesting here will do in follow up polish
📖 Storybook Preview |
| typeof content === 'string' || typeof content === 'number'; | ||
|
|
||
| const hasContent = (content: React.ReactNode) => | ||
| content !== null && content !== undefined; |
There was a problem hiding this comment.
hasContent mishandles false, causing incorrect description spacing
Medium Severity
hasContent only rejects null and undefined, so hasContent(false) returns true. A common React pattern like title={condition && "Title"} passes false when the condition fails. On line 97, hasContent(title) then incorrectly evaluates to true, applying mt-1 margin to the description even though no title is visually rendered — producing incorrect spacing.
Additional Locations (1)
## Release 25.0.0 This release includes new components migrated from Extension and Mobile, breaking API improvements to ButtonIcon and Input components, and continued ADR-0003/0004 type migrations. ### 📦 Package Versions - `@metamask/design-system-shared`: **0.4.0** - `@metamask/design-system-react`: **0.11.0** - `@metamask/design-system-react-native`: **0.11.0** ### 🔄 Shared Type Updates (0.4.0) #### Component Type Additions (#964, #955) Added shared types for newly migrated components following ADR-0003 and ADR-0004 patterns: **What Changed:** - Added `ButtonFilter` shared types with `ButtonFilterVariant` const object and `ButtonFilterPropsShared` - Added `BannerBase` shared types with `BannerBaseSeverity` const object and `BannerBasePropsShared` **Impact:** - Enables consistent ButtonFilter and BannerBase implementations across React and React Native - Continues ADR-0003/0004 const-object + string-union pattern adoption ### 🌐 React Web Updates (0.11.0) #### Added - Added `ButtonFilter` component for filter button functionality (#964) - Added `BannerBase` component for creating custom banner notifications (#961) #### Changed - **BREAKING:** Updated `ButtonIcon` API to use `variant` prop instead of `isInverse` and `isFloating` boolean props (#948) - Updated `Ai` icon to filled version for visual consistency (#970) ### 📱 React Native Updates (0.11.0) #### Added - Added `ButtonFilter` component (#964) - Added `BottomSheet` component for modal interactions (#963) - Added `MainActionButton` component for primary CTAs (#952) - Added `BannerBase` component for custom banners (#955) - Added `ListItem` component for standardized list rows (#958) - Added `TabEmptyState` component for empty states (#949) - Added `BottomSheetDialog` component for dialogs (#905) #### Changed - **BREAKING:** Updated `ButtonIcon` API to use `variant` prop instead of `isInverse` and `isFloating` boolean props (#948) - **BREAKING:** `Input` component now requires `value` prop and is controlled-only (#960) - Updated `Ai` icon to filled version (#970) #### Fixed - Fixed iOS placeholder alignment issue in `Input` component (#960) - Fixed missing component exports (#967) ###⚠️ Breaking Changes #### ButtonIcon Variant Prop (Both Platforms) **What Changed:** - Removed `isInverse` and `isFloating` boolean props - Added `variant` prop with three options: - `ButtonIconVariant.Default` (transparent background - default) - `ButtonIconVariant.Filled` (muted background with rounded corners - new) - `ButtonIconVariant.Floating` (colored background with inverse icon - replaces `isFloating`) **Migration:** ```tsx // Before <ButtonIcon name={IconName.Add} isFloating /> // After <ButtonIcon name={IconName.Add} variant={ButtonIconVariant.Floating} /> ``` See migration guides for complete instructions: - [React Migration Guide](./packages/design-system-react/MIGRATION.md#from-version-0100-to-0110) - [React Native Migration Guide](./packages/design-system-react-native/MIGRATION.md#from-version-0100-to-0110) #### Input Controlled-Only (React Native Only) **What Changed:** - Removed `defaultValue` prop - `value` prop is now required - All Input instances must be controlled with state management **Migration:** ```tsx // Before <Input placeholder="Enter text" defaultValue="Initial" onChange={handleChange} /> // After const [text, setText] = useState('Initial'); <Input placeholder="Enter text" value={text} onChange={setText} /> ``` **Impact:** - Affects `Input` and `TextField` components - Provides consistent behavior and fixes iOS placeholder alignment See [React Native Migration Guide](./packages/design-system-react-native/MIGRATION.md#from-version-0100-to-0110) for complete instructions. ### ✅ Checklist - [x] Changelogs updated with human-readable descriptions - [x] Changelog validation passed (`yarn changelog:validate`) - [x] Version bumps follow semantic versioning - design-system-shared: minor (0.3.0 → 0.4.0) - new shared types - design-system-react: minor (0.10.0 → 0.11.0) - new components + breaking ButtonIcon change - design-system-react-native: minor (0.10.0 → 0.11.0) - new components + breaking ButtonIcon and Input changes - [x] Breaking changes documented with migration guidance - [x] Migration guides updated with before/after examples - [x] PR references included in changelog entries <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Although this PR is mostly versioning/docs, it formalizes breaking public API changes (`ButtonIcon` and RN `Input`) that will require downstream updates and could cause consumer build failures if missed. > > **Overview** > Bumps the monorepo release to `25.0.0` and publishes new package versions (`@metamask/design-system-react@0.11.0`, `@metamask/design-system-react-native@0.11.0`, `@metamask/design-system-shared@0.4.0`) with updated changelogs. > > Documents newly added components/types (`ButtonFilter`, `BannerBase`, plus several React Native-only additions like `BottomSheet`, `ListItem`, etc.) and **breaking API changes**: `ButtonIcon` replaces `isInverse`/`isFloating` with a `variant` prop (React + RN), and React Native `Input` becomes controlled-only (requires `value`, removes `defaultValue`). Migration guides are updated with before/after examples and new compare links. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 0148a27. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->



Description
BannerBaseinto MMDS for@metamask/design-system-react-native.@metamask/design-system-sharedviaBannerBasePropsShared(ADR-0004 layering).Box,Text,Button,ButtonIcon) with default primary action button behavior.Scope note:
Related issues
Fixes:
Manual testing steps
yarn buildfrom repo root.yarn testfrom repo root.yarn lintfrom repo root.BannerBasestories:Default,Title,Description,Children,ActionButtonOnPress,OnClose,StartAccessory.DefaultandActionButtonOnPressrender an action button.Screenshots/Recordings
Before
BannerBase in extension
bannerbase.ext720.mov
BannerBase in mobile
bannerbasemobile.720.mov
After
@metamask/design-system-react-native.@metamask/design-system-shared.Stories
storybook720.mov
READMES
docs720.mov
Pre-merge author checklist
Pre-merge reviewer checklist
Note
Low Risk
Mostly additive: introduces a new component and shared types with Storybook/docs/tests, and updates public exports. Low risk aside from potential minor API/export or styling regressions for new consumers.
Overview
Adds a new
BannerBasecomponent to@metamask/design-system-react-native, including its typed API (BannerBase.types.ts), unit tests, README documentation, and Storybook stories, and wires it into the package exports.Introduces cross-platform
BannerBasePropsSharedin@metamask/design-system-sharedand exports it, and updates React Native Storybook’s auto-generatedstorybook.requires.jsto include the new stories.Written by Cursor Bugbot for commit bd3012a. This will update automatically on new commits. Configure here.