From 307aa050998afe6ef59feaebf1fbfd1e01fa873d Mon Sep 17 00:00:00 2001 From: Brian August Nguyen Date: Sun, 14 Dec 2025 22:58:45 -0800 Subject: [PATCH 1/5] chore: update HeaderBase to support new Headers (#23931) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** This PR refactors the `HeaderBase` component to use `@metamask/design-system-react-native` components and Tailwind CSS styling. **Reason for change:** - The existing `HeaderBase` component uses custom StyleSheet-based styling which doesn't align with the design system migration goals - The component API could be simplified for common use cases like adding back/close buttons **Improvements:** - Migrates to design system components (`Box`, `Text`, `ButtonIcon`) and Tailwind CSS (`twClassName`) - Adds new simplified APIs: `startButtonIconProps` and `endButtonIconProps[]` for common icon button use cases without needing to create custom accessories - Adds `twClassName` prop for Tailwind-based customization - Adds optional `testID` prop for better test flexibility - Removes `HeaderBase.styles.ts` in favor of inline Tailwind-based layout - Updates text variants to use design system variants (`HeadingSm`/`HeadingLg`) - Re-exports test IDs and types from `index.ts` for easier imports ## **Changelog** CHANGELOG entry: null ## **Related issues** Fixes: ## **Manual testing steps** ```gherkin Feature: HeaderBase component Scenario: user views a header with Compact variant (default) Given the app is open When user navigates to a screen using HeaderBase with Compact variant Then the title should be center-aligned with HeadingSm text Scenario: user views a header with Display variant Given the app is open When user navigates to a screen using HeaderBase with Display variant Then the title should be left-aligned with HeadingLg text Scenario: user taps header icon buttons Given a screen has HeaderBase with startButtonIconProps and endButtonIconProps When user taps the back icon Then the associated onPress handler should be called ``` ## **Screenshots/Recordings** ### **Before** ### **After** https://github.com/user-attachments/assets/c99e5a85-c96e-48a2-b245-0205480f413c Existing usages is still functional https://github.com/user-attachments/assets/4cffee2c-9fb5-4a50-a459-1c4062eec004 ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [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 - [x] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.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. --- > [!NOTE] > Refactors `HeaderBase` to use design-system components with Tailwind styling, adds simplified icon-button APIs, removes legacy styles, and updates tests/snapshots and usages across the app. > > - **Component Library — HeaderBase**: > - Migrate to `@metamask/design-system-react-native` (`Box`, `Text`, `ButtonIcon`) and Tailwind via `twClassName`. > - Add `startButtonIconProps` and `endButtonIconProps[]`; prioritize custom accessories when both provided. > - Add `testID` and `twClassName` props; re-export test IDs and types from `index.ts`. > - Map variants to design-system text (`HeadingSm`/`HeadingLg`); remove `HeaderBase.styles.ts` in favor of inline Tailwind. > - Implement equal-width accessory wrappers for centered titles; support multiple end buttons (reversed render order). > - **Stories/Docs**: > - Rewrite stories showcasing variants, Tailwind usage, button-prop variants, and custom children. > - Update README with new props, usage, and defaults. > - **Tests**: > - Overhaul `HeaderBase.test.tsx` to new API/behavior; remove obsolete alignment tests in `BottomSheetHeader`. > - Update mocks for Tailwind and design-system variants; export test IDs for easier querying. > - **Consumers/Styling**: > - Apply `twClassName="h-auto"` or header tweaks in `Navbar`, `TokensFullView`, `UpdateNeeded`, and clean up redundant header styles. > - **Snapshots**: > - Widespread snapshot updates reflecting new layout/typography (center/left alignment, `height: 48`, padding separation, fontWeight 700, flex wrappers). > > Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit b995e9e08ddf3e66927528a2a4b93a8d1627c97e. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot). --- .../BottomSheetHeader.test.tsx | 37 -- .../BottomSheetHeader.test.tsx.snap | 120 ++-- .../HeaderBase/HeaderBase.constants.ts | 19 +- .../HeaderBase/HeaderBase.stories.tsx | 248 +++++--- .../HeaderBase/HeaderBase.styles.ts | 57 -- .../components/HeaderBase/HeaderBase.test.tsx | 536 ++++++++++-------- .../components/HeaderBase/HeaderBase.tsx | 194 +++++-- .../components/HeaderBase/HeaderBase.types.ts | 66 ++- .../components/HeaderBase/README.md | 92 ++- .../__snapshots__/HeaderBase.test.tsx.snap | 124 ---- .../components/HeaderBase/index.ts | 5 + .../__snapshots__/form.test.ts.snap | 114 ++-- .../BridgeDestNetworkSelector.test.tsx.snap | 57 +- .../BridgeDestTokenSelector.test.tsx.snap | 57 +- .../BridgeSourceNetworkSelector.test.tsx.snap | 57 +- .../BridgeSourceTokenSelector.test.tsx.snap | 57 +- .../QuoteExpiredModal.test.tsx.snap | 57 +- .../__snapshots__/SlippageModal.test.tsx.snap | 57 +- .../AddFundsBottomSheet.test.tsx.snap | 144 +++-- .../LendingLearnMoreModal.test.tsx.snap | 36 +- .../__snapshots__/EarnTokenList.test.tsx.snap | 20 +- .../__snapshots__/MaxInputModal.test.tsx.snap | 36 +- .../LendingMaxWithdrawalModal.test.tsx.snap | 36 +- app/components/UI/Navbar/index.js | 1 + .../__snapshots__/index.test.tsx.snap | 20 +- .../NetworkVerificationInfo.test.tsx.snap | 40 +- .../PerpsHomeView/PerpsHomeView.test.tsx | 4 + .../PerpsBottomSheetTooltip.test.tsx.snap | 20 +- .../__snapshots__/Checkout.test.tsx.snap | 360 ++++++------ .../__snapshots__/SettingsModal.test.tsx.snap | 57 +- .../Quotes/__snapshots__/Quotes.test.tsx.snap | 171 +++--- .../FiatSelectorModal.test.tsx.snap | 144 +++-- ...ncompatibleAccountTokenModal.test.tsx.snap | 36 +- .../PaymentMethodSelectorModal.test.tsx.snap | 108 ++-- .../RegionSelectorModal.test.tsx.snap | 396 ++++++------- .../TokenSelectModal.test.tsx.snap | 36 +- .../UnsupportedRegionModal.test.tsx.snap | 40 +- .../ConfigurationModal.test.tsx.snap | 57 +- .../ErrorDetailsModal.test.tsx.snap | 36 +- ...ncompatibleAccountTokenModal.test.tsx.snap | 36 +- .../PaymentMethodSelectorModal.test.tsx.snap | 57 +- .../RegionSelectorModal.test.tsx.snap | 456 ++++++++------- .../__snapshots__/SsnInfoModal.test.tsx.snap | 36 +- .../StateSelectorModal.test.tsx.snap | 216 ++++--- .../TokenSelectorModal.test.tsx.snap | 108 ++-- .../UnsupportedRegionModal.test.tsx.snap | 72 ++- .../UnsupportedStateModal.test.tsx.snap | 36 +- .../__snapshots__/WebviewModal.test.tsx.snap | 72 ++- .../EligibilityFailedModal.test.tsx.snap | 36 +- .../RampUnsupportedModal.test.tsx.snap | 36 +- .../UnsupportedTokenModal.test.tsx.snap | 57 +- .../GasImpactModal.test.tsx.snap | 36 +- .../PoolStakingLearnMoreModal.test.tsx.snap | 36 +- .../UI/UpdateNeeded/UpdateNeeded.tsx | 1 + .../__snapshots__/UpdateNeeded.test.tsx.snap | 34 +- ...tPermissionsConfirmRevokeAll.test.tsx.snap | 41 +- .../ConnectionDetails.test.tsx.snap | 41 +- .../PermittedNetworksInfoSheet.test.tsx.snap | 41 +- .../__snapshots__/index.test.tsx.snap | 38 +- .../AddressSelector.test.tsx.snap | 57 +- .../Asset/__snapshots__/index.test.js.snap | 330 +++++------ .../AccountTypes/BaseAccountDetails/styles.ts | 4 - .../SmartAccountModal.styles.ts | 4 - .../RpcSelectionModal.test.tsx.snap | 20 +- .../Views/NftFullView/NftFullView.test.tsx | 6 +- .../TokensFullView/TokensFullView.test.tsx | 6 +- .../Views/TokensFullView/TokensFullView.tsx | 1 + .../Wallet/__snapshots__/index.test.tsx.snap | 135 +++-- 68 files changed, 3027 insertions(+), 2774 deletions(-) delete mode 100644 app/component-library/components/HeaderBase/HeaderBase.styles.ts delete mode 100644 app/component-library/components/HeaderBase/__snapshots__/HeaderBase.test.tsx.snap diff --git a/app/component-library/components/BottomSheets/BottomSheetHeader/BottomSheetHeader.test.tsx b/app/component-library/components/BottomSheets/BottomSheetHeader/BottomSheetHeader.test.tsx index 17f2d251fc64..8a9e3683f644 100644 --- a/app/component-library/components/BottomSheets/BottomSheetHeader/BottomSheetHeader.test.tsx +++ b/app/component-library/components/BottomSheets/BottomSheetHeader/BottomSheetHeader.test.tsx @@ -101,43 +101,6 @@ describe('BottomSheetHeader', () => { ); }); - it('applies compact variant by default', () => { - const { getByTestId } = render( - Header Content, - ); - - const titleElement = getByTestId('header-title'); - expect(titleElement.props.style.textAlign).toBe('center'); - }); - - it('applies display variant when variant prop is set to Display', () => { - const { getByTestId } = render( - - Header Content - , - ); - - const titleElement = getByTestId('header-title'); - expect(titleElement.props.style.textAlign).toBe('left'); - }); - - it('applies compact variant when variant prop is set to Compact', () => { - const { getByTestId } = render( - - Header Content - , - ); - - const titleElement = getByTestId('header-title'); - expect(titleElement.props.style.textAlign).toBe('center'); - }); - it('renders snapshot correctly with Display variant', () => { const wrapper = render( diff --git a/app/component-library/components/BottomSheets/BottomSheetHeader/__snapshots__/BottomSheetHeader.test.tsx.snap b/app/component-library/components/BottomSheets/BottomSheetHeader/__snapshots__/BottomSheetHeader.test.tsx.snap index c0d1e72bd2c0..22dc1eaf2d64 100644 --- a/app/component-library/components/BottomSheets/BottomSheetHeader/__snapshots__/BottomSheetHeader.test.tsx.snap +++ b/app/component-library/components/BottomSheets/BottomSheetHeader/__snapshots__/BottomSheetHeader.test.tsx.snap @@ -5,34 +5,49 @@ exports[`BottomSheetHeader renders snapshot correctly with Compact variant 1`] = style={ [ { + "alignItems": "center", "flexDirection": "row", "gap": 16, - "padding": 16, + "height": 48, }, false, + { + "padding": 16, + }, ] } testID="header" > @@ -47,34 +62,46 @@ exports[`BottomSheetHeader renders snapshot correctly with Display variant 1`] = style={ [ { + "alignItems": "center", "flexDirection": "row", "gap": 16, - "padding": 16, }, false, + { + "padding": 16, + }, ] } testID="header" > @@ -89,34 +116,49 @@ exports[`BottomSheetHeader should render snapshot correctly 1`] = ` style={ [ { + "alignItems": "center", "flexDirection": "row", "gap": 16, - "padding": 16, + "height": 48, }, false, + { + "padding": 16, + }, ] } testID="header" > diff --git a/app/component-library/components/HeaderBase/HeaderBase.constants.ts b/app/component-library/components/HeaderBase/HeaderBase.constants.ts index 840f7053842e..8408ea0953b2 100644 --- a/app/component-library/components/HeaderBase/HeaderBase.constants.ts +++ b/app/component-library/components/HeaderBase/HeaderBase.constants.ts @@ -1,15 +1,22 @@ // External dependencies. -import { TextVariant } from '../Texts/Text'; +import { TextVariant } from '@metamask/design-system-react-native'; // Internal dependencies. import { HeaderBaseVariant } from './HeaderBase.types'; -// Text variants for different header variants -export const HEADERBASE_VARIANT_TEXT_VARIANTS = { - [HeaderBaseVariant.Display]: TextVariant.HeadingLG, - [HeaderBaseVariant.Compact]: TextVariant.HeadingSM, +/** + * Text variant mapping based on HeaderBase variant. + */ +export const HEADERBASE_VARIANT_TEXT_VARIANTS: Record< + HeaderBaseVariant, + TextVariant +> = { + [HeaderBaseVariant.Compact]: TextVariant.HeadingSm, + [HeaderBaseVariant.Display]: TextVariant.HeadingLg, }; -// Test IDs +/** + * Default test IDs for HeaderBase component. + */ export const HEADERBASE_TEST_ID = 'header'; export const HEADERBASE_TITLE_TEST_ID = 'header-title'; diff --git a/app/component-library/components/HeaderBase/HeaderBase.stories.tsx b/app/component-library/components/HeaderBase/HeaderBase.stories.tsx index 4afb9ac5b6d1..0be69bd7b593 100644 --- a/app/component-library/components/HeaderBase/HeaderBase.stories.tsx +++ b/app/component-library/components/HeaderBase/HeaderBase.stories.tsx @@ -1,121 +1,215 @@ -/* eslint-disable react/display-name */ -/* eslint-disable react/prop-types */ /* eslint-disable no-console */ - -// Third party dependencies. import React from 'react'; -// External dependencies. -import Button, { ButtonVariants } from '../Buttons/Button'; -import ButtonIcon from '../Buttons/ButtonIcon'; -import { IconName, IconColor } from '../Icons/Icon'; +import { + Box, + Text, + TextVariant, + ButtonIcon, + ButtonIconSize, + IconName, +} from '@metamask/design-system-react-native'; -// Internal dependencies. -import { default as HeaderBaseComponent } from './HeaderBase'; +import HeaderBase from './HeaderBase'; import { HeaderBaseVariant } from './HeaderBase.types'; -const HeaderBaseStoryMeta = { +const HeaderBaseMeta = { title: 'Component Library / HeaderBase', - component: HeaderBaseComponent, + component: HeaderBase, + argTypes: { + children: { + control: 'text', + }, + variant: { + control: 'select', + options: Object.values(HeaderBaseVariant), + }, + twClassName: { + control: 'text', + }, + }, }; -export default HeaderBaseStoryMeta; +export default HeaderBaseMeta; + +export const Default = { + args: { + children: 'Header Title', + variant: HeaderBaseVariant.Compact, + }, +}; -export const HeaderBase = { +export const Variant = { render: () => ( - { - console.log('clicked'); - }} - /> - } - endAccessory={ -