Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .storybook/storybook.requires.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,6 @@ android {
dependencies {
// The version of react-native is set by the React Native Gradle Plugin
implementation(files("../libs/ecies.aar"))
implementation(files("../libs/nativesdk.aar"))
implementation("com.facebook.react:react-android")
implementation 'org.apache.commons:commons-compress:1.22'
androidTestImplementation 'androidx.test:core:1.5.0'
Expand Down
6 changes: 0 additions & 6 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -184,11 +184,5 @@
android:resource="@xml/filepaths"
/>
</provider>
<service
android:name="io.metamask.nativesdk.MessageService"
android:enabled="true"
android:exported="true">

</service>
</application>
</manifest>
4 changes: 1 addition & 3 deletions android/app/src/main/java/io/metamask/MainApplication.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import cl.json.ShareApplication
import io.branch.rnbranch.RNBranchModule
import io.metamask.nativeModules.PreventScreenshotPackage
import io.metamask.nativeModules.RCTMinimizerPackage
import io.metamask.nativesdk.NativeSDKPackage
import io.metamask.nativeModules.RNTar.RNTarPackage
import io.metamask.nativeModules.NotificationPackage

Expand All @@ -43,9 +42,8 @@ class MainApplication : Application(), ShareApplication, ReactApplication {
// Add all our custom packages
packages.add(PreventScreenshotPackage())
packages.add(RCTMinimizerPackage())
packages.add(NativeSDKPackage())
packages.add(RNTarPackage())
packages.add(NotificationPackage())
packages.add(NotificationPackage())
return packages
}

Expand Down
Binary file removed android/libs/nativesdk.aar
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/* eslint-disable no-console */
import React from 'react';

import {
Box,
Text,
TextVariant,
IconName,
} from '@metamask/design-system-react-native';

import HeaderWithTitleLeft from './HeaderWithTitleLeft';

const HeaderWithTitleLeftMeta = {
title: 'Components Temp / HeaderWithTitleLeft',
component: HeaderWithTitleLeft,
argTypes: {
twClassName: {
control: 'text',
},
},
};

export default HeaderWithTitleLeftMeta;

const SampleNFTImage = () => (
<Box twClassName="w-12 h-12 rounded-lg bg-success-muted items-center justify-center">
<Text variant={TextVariant.BodySm}>NFT</Text>
</Box>
);

export const Default = {
args: {
titleLeftProps: {
topLabel: 'Send',
title: '$4.42',
},
},
};

export const OnBack = {
render: () => (
<HeaderWithTitleLeft
onBack={() => console.log('Back pressed')}
titleLeftProps={{
topLabel: 'Send',
title: '$4.42',
endAccessory: <SampleNFTImage />,
}}
/>
),
};

export const WithBottomLabel = {
render: () => (
<HeaderWithTitleLeft
onBack={() => console.log('Back pressed')}
titleLeftProps={{
topLabel: 'Send',
title: '$4.42',
bottomLabel: '0.002 ETH',
endAccessory: <SampleNFTImage />,
}}
/>
),
};

export const EndButtonIconProps = {
render: () => (
<HeaderWithTitleLeft
onBack={() => console.log('Back pressed')}
endButtonIconProps={[
{
iconName: IconName.Close,
onPress: () => console.log('Close pressed'),
},
]}
titleLeftProps={{
topLabel: 'Send',
title: '$4.42',
endAccessory: <SampleNFTImage />,
}}
/>
),
};

export const BackButtonProps = {
render: () => (
<HeaderWithTitleLeft
backButtonProps={{
onPress: () => console.log('Custom back pressed'),
}}
titleLeftProps={{
topLabel: 'Receive',
title: '$1,234.56',
}}
/>
),
};

export const TitleLeft = {
render: () => (
<HeaderWithTitleLeft
onBack={() => console.log('Back pressed')}
titleLeft={
<Box twClassName="px-4 py-2">
<Text variant={TextVariant.HeadingMd}>Custom Title Section</Text>
<Text variant={TextVariant.BodySm}>
This is a completely custom title section
</Text>
</Box>
}
/>
),
};

export const NoBackButton = {
render: () => (
<HeaderWithTitleLeft
titleLeftProps={{
topLabel: 'Account Balance',
title: '$12,345.67',
bottomLabel: '+$123.45 (1.2%)',
}}
/>
),
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
// Third party dependencies.
import React from 'react';
import { render, fireEvent } from '@testing-library/react-native';
import { Text, IconName } from '@metamask/design-system-react-native';

// Internal dependencies.
import HeaderWithTitleLeft from './HeaderWithTitleLeft';

const TEST_IDS = {
CONTAINER: 'header-with-title-left-container',
TITLE_SECTION: 'header-with-title-left-title-section',
BACK_BUTTON: 'header-with-title-left-back-button',
TITLE_LEFT: 'title-left',
HEADER_BASE_END_ACCESSORY: 'header-base-end-accessory',
};

describe('HeaderWithTitleLeft', () => {
beforeEach(() => {
jest.clearAllMocks();
});

describe('rendering', () => {
it('renders container with correct testID', () => {
const { getByTestId } = render(
<HeaderWithTitleLeft
testID={TEST_IDS.CONTAINER}
titleLeftProps={{ title: 'Test' }}
/>,
);

expect(getByTestId(TEST_IDS.CONTAINER)).toBeOnTheScreen();
});

it('renders title section when titleLeftProps provided', () => {
const { getByTestId } = render(
<HeaderWithTitleLeft
titleSectionTestID={TEST_IDS.TITLE_SECTION}
titleLeftProps={{ title: '$4.42' }}
/>,
);

expect(getByTestId(TEST_IDS.TITLE_SECTION)).toBeOnTheScreen();
});

it('renders TitleLeft with props when titleLeftProps provided', () => {
const { getByText, getByTestId } = render(
<HeaderWithTitleLeft
titleLeftProps={{
topLabel: 'Send',
title: '$4.42',
testID: TEST_IDS.TITLE_LEFT,
}}
/>,
);

expect(getByText('Send')).toBeOnTheScreen();
expect(getByText('$4.42')).toBeOnTheScreen();
expect(getByTestId(TEST_IDS.TITLE_LEFT)).toBeOnTheScreen();
});

it('renders custom titleLeft node when provided', () => {
const { getByText } = render(
<HeaderWithTitleLeft titleLeft={<Text>Custom Title Section</Text>} />,
);

expect(getByText('Custom Title Section')).toBeOnTheScreen();
});

it('titleLeft takes priority over titleLeftProps', () => {
const { getByText, queryByText } = render(
<HeaderWithTitleLeft
titleLeft={<Text>Custom Node</Text>}
titleLeftProps={{ title: 'Props Title' }}
/>,
);

expect(getByText('Custom Node')).toBeOnTheScreen();
expect(queryByText('Props Title')).toBeNull();
});

it('does not render title section when neither titleLeft nor titleLeftProps provided', () => {
const { queryByTestId } = render(
<HeaderWithTitleLeft
onBack={jest.fn()}
titleSectionTestID={TEST_IDS.TITLE_SECTION}
/>,
);

expect(queryByTestId(TEST_IDS.TITLE_SECTION)).toBeNull();
});
});

describe('back button', () => {
it('renders back button when onBack provided', () => {
const { getByTestId } = render(
<HeaderWithTitleLeft
onBack={jest.fn()}
backButtonProps={{ testID: TEST_IDS.BACK_BUTTON }}
titleLeftProps={{ title: 'Test' }}
/>,
);

expect(getByTestId(TEST_IDS.BACK_BUTTON)).toBeOnTheScreen();
});

it('renders back button when backButtonProps provided', () => {
const { getByTestId } = render(
<HeaderWithTitleLeft
backButtonProps={{ onPress: jest.fn(), testID: TEST_IDS.BACK_BUTTON }}
titleLeftProps={{ title: 'Test' }}
/>,
);

expect(getByTestId(TEST_IDS.BACK_BUTTON)).toBeOnTheScreen();
});

it('calls onBack when back button pressed', () => {
const onBack = jest.fn();
const { getByTestId } = render(
<HeaderWithTitleLeft
onBack={onBack}
backButtonProps={{ testID: TEST_IDS.BACK_BUTTON }}
titleLeftProps={{ title: 'Test' }}
/>,
);

fireEvent.press(getByTestId(TEST_IDS.BACK_BUTTON));

expect(onBack).toHaveBeenCalledTimes(1);
});

it('calls backButtonProps.onPress when back button pressed', () => {
const onPress = jest.fn();
const { getByTestId } = render(
<HeaderWithTitleLeft
backButtonProps={{ onPress, testID: TEST_IDS.BACK_BUTTON }}
titleLeftProps={{ title: 'Test' }}
/>,
);

fireEvent.press(getByTestId(TEST_IDS.BACK_BUTTON));

expect(onPress).toHaveBeenCalledTimes(1);
});

it('backButtonProps.onPress takes priority over onBack', () => {
const onBack = jest.fn();
const onPress = jest.fn();
const { getByTestId } = render(
<HeaderWithTitleLeft
onBack={onBack}
backButtonProps={{ onPress, testID: TEST_IDS.BACK_BUTTON }}
titleLeftProps={{ title: 'Test' }}
/>,
);

fireEvent.press(getByTestId(TEST_IDS.BACK_BUTTON));

expect(onPress).toHaveBeenCalledTimes(1);
expect(onBack).not.toHaveBeenCalled();
});

it('does not render back button when neither onBack nor backButtonProps provided', () => {
const { queryByLabelText } = render(
<HeaderWithTitleLeft titleLeftProps={{ title: 'Test' }} />,
);

expect(queryByLabelText('Arrow Left')).toBeNull();
});
});

describe('props forwarding', () => {
it('forwards endButtonIconProps to HeaderBase', () => {
const { getByTestId } = render(
<HeaderWithTitleLeft
onBack={jest.fn()}
endButtonIconProps={[
{
iconName: IconName.Close,
onPress: jest.fn(),
testID: TEST_IDS.HEADER_BASE_END_ACCESSORY,
},
]}
titleLeftProps={{ title: 'Test' }}
/>,
);

expect(getByTestId(TEST_IDS.HEADER_BASE_END_ACCESSORY)).toBeOnTheScreen();
});

it('accepts custom testID', () => {
const { getByTestId } = render(
<HeaderWithTitleLeft
testID="custom-header"
titleLeftProps={{ title: 'Test' }}
/>,
);

expect(getByTestId('custom-header')).toBeOnTheScreen();
});

it('forwards startButtonIconProps directly when provided', () => {
const onPress = jest.fn();
const { getByTestId } = render(
<HeaderWithTitleLeft
startButtonIconProps={{
iconName: IconName.Menu,
onPress,
testID: 'custom-start-button',
}}
titleLeftProps={{ title: 'Test' }}
/>,
);

expect(getByTestId('custom-start-button')).toBeOnTheScreen();
});
});
});
Loading
Loading