Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
1ebfa20
style: update confirmation page background colors (#26512)
bschorchit Mar 11, 2026
9a469ab
feat: migrate Label (earn scope) (#27260)
kirillzyusko Mar 11, 2026
71625c2
test: skips ReactNative bundle reload on swap test (#27051)
christopherferreira9 Mar 11, 2026
a2ada6a
fix: block explorer url was using global chain id and not tx chain id…
vinnyhoward Mar 11, 2026
f263b21
fix: scanning verbiage only shows when actually scanning (#27319)
vinnyhoward Mar 11, 2026
c8205ab
fix(homepage): restructure perps market tile card header layout (#27266)
wachunei Mar 11, 2026
2c4116e
test: adds diagnosis for when Detox's internal port is already in use…
christopherferreira9 Mar 11, 2026
f4f4903
refactor(predict): make Predict sports UI generic via league config m…
matallui Mar 11, 2026
d69f7dc
perf(predict): lazy per-team loading in PolymarketProvider (#27304)
matallui Mar 11, 2026
7a2ee9e
feat: balance sort on homepage and enable popular networks on focus (…
salimtb Mar 11, 2026
b0e23ee
test: fixed e2e tests affected by the new homescreen redesign (#26692)
javiergarciavera Mar 11, 2026
75b3d13
fix: adjust bridge token selector ticker font (#27357)
bfullam Mar 11, 2026
4701a2b
refactor: remove remaining occurrences of BIP44 flag (#27344)
gantunesr Mar 11, 2026
d34949e
test: moves hardcoded testIds into dedicated files - accounts (#27141)
christopherferreira9 Mar 11, 2026
e91116e
fix: amount validation on end page to make 0. and . valid values (#27…
jpuri Mar 11, 2026
65238f0
docs: Add IMPORTANT callout for deeplinks (#27284)
Montoya Mar 11, 2026
8efdfbd
feat(homepage): add SectionHeader to components-temp and adopt across…
vinnyhoward Mar 11, 2026
0c487d3
refactor(predict): migrate usePredictAccountState to React Query (#26…
Kureev Mar 11, 2026
2513c6f
chore: Bump Snaps dependencies (#27335)
Mrtenz Mar 11, 2026
bb1f415
chore: bump `@metamask/profile-sync-controller` to `^28.0.0` (#27320)
mathieuartu Mar 11, 2026
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const ACCOUNT_LIST_CELL_TEST_IDS = {
ACCOUNT_LIST_CELL: 'account-list-cell-checkbox-',
} as const;
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import AccountCell from '../../AccountCell';
import createStyles from '../MultichainAccountSelectorList.styles';
import { AccountListCellProps } from './AccountListCell.types';
import Checkbox from '../../../../components/Checkbox';
import { ACCOUNT_LIST_CELL_TEST_IDS } from './AccountListCell.testIds';

const AccountListCell = memo(
({
Expand All @@ -31,7 +32,9 @@ const AccountListCell = memo(
<AccountCell
startAccessory={
showCheckbox ? (
<View testID={`account-list-cell-checkbox-${accountGroup.id}`}>
<View
testID={`${ACCOUNT_LIST_CELL_TEST_IDS.ACCOUNT_LIST_CELL}${accountGroup.id}`}
>
<Checkbox isChecked={isSelected} onPress={handlePress} />
</View>
) : undefined
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const EXTERNAL_ACCOUNT_CELL_TEST_IDS = {
CONTAINER: 'external-account-cell-touchable',
} as const;
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { getNetworkImageSource } from '../../../../../util/networks';
import { selectAvatarAccountType } from '../../../../../selectors/settings';
import { strings } from '../../../../../../locales/i18n';
import createStyles from '../MultichainAccountSelectorList.styles';
import { EXTERNAL_ACCOUNT_CELL_TEST_IDS } from './ExternalAccountCell.testIds';

/**
* ExternalAccountCell Component
Expand Down Expand Up @@ -56,7 +57,7 @@ const ExternalAccountCell: React.FC<ExternalAccountCellProps> = ({
styles.externalAccountContainer,
isDisabled && styles.externalAccountContainerDisabled,
]}
testID="external-account-cell-touchable"
testID={EXTERNAL_ACCOUNT_CELL_TEST_IDS.CONTAINER}
>
<AvatarAccount
accountAddress={address}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/* eslint-disable no-console */
import React from 'react';
import { Text } from 'react-native';

import {
BoxJustifyContent,
Icon,
IconName,
IconSize,
IconColor,
} from '@metamask/design-system-react-native';

import SectionHeader from './SectionHeader';

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

export default SectionHeaderMeta;

export const Default = {
args: {
title: 'Tokens',
onPress: () => console.log('View all pressed'),
},
};

export const StaticNoPress = {
render: () => <SectionHeader title="Portfolio" />,
};

export const WithEndAccessory = {
render: () => (
<SectionHeader
title="DeFi"
onPress={() => console.log('View all DeFi')}
endAccessory={
<Icon
name={IconName.Info}
size={IconSize.Sm}
color={IconColor.IconAlternative}
/>
}
/>
),
};

export const WithReactNodeTitle = {
render: () => (
<SectionHeader
title={<Text>Custom Node Title</Text>}
onPress={() => console.log('Pressed')}
/>
),
};

export const WithCustomEndIcon = {
render: () => (
<SectionHeader
title="NFTs"
onPress={() => console.log('View all NFTs')}
endIconName={IconName.Arrow2Right}
/>
),
};

export const WithCustomEndIconColor = {
render: () => (
<SectionHeader
title="Perps"
onPress={() => console.log('View all Perps')}
endIconColor={IconColor.IconDefault}
/>
),
};

export const WithJustifyContentBetween = {
render: () => (
<SectionHeader
title="Your positions"
justifyContent={BoxJustifyContent.Between}
endAccessory={
<Icon
name={IconName.MoreVertical}
size={IconSize.Sm}
color={IconColor.IconAlternative}
/>
}
/>
),
};

export const FullExample = {
render: () => (
<SectionHeader
title="Tokens"
onPress={() => console.log('View all tokens')}
endAccessory={
<Icon
name={IconName.Info}
size={IconSize.Sm}
color={IconColor.IconAlternative}
/>
}
/>
),
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
// Third party dependencies.
import React from 'react';
import { render, fireEvent } from '@testing-library/react-native';
import { Text } from 'react-native';

// External dependencies.
import {
BoxJustifyContent,
IconColor,
IconName,
} from '@metamask/design-system-react-native';

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

jest.mock('@metamask/design-system-twrnc-preset', () => ({
useTailwind: () => ({ style: () => ({}) }),
}));

// ButtonIcon's built-in testID from the design system
const BUTTON_ICON_TEST_ID = 'button-icon';
const CONTAINER_TEST_ID = 'section-header-container';

describe('SectionHeader', () => {
describe('rendering', () => {
it('renders with a string title', () => {
const { getByText } = render(<SectionHeader title="Tokens" />);

expect(getByText('Tokens')).toBeOnTheScreen();
});

it('renders with a React node title', () => {
const { getByTestId, getByText } = render(
<SectionHeader
title={<Text testID="custom-title">Custom Title</Text>}
/>,
);

expect(getByTestId('custom-title')).toBeOnTheScreen();
expect(getByText('Custom Title')).toBeOnTheScreen();
});

it('renders with testID on the container', () => {
const { getByTestId } = render(
<SectionHeader title="Tokens" testID={CONTAINER_TEST_ID} />,
);

expect(getByTestId(CONTAINER_TEST_ID)).toBeOnTheScreen();
});
});

describe('onPress', () => {
it('does not render trailing icon when onPress is not provided', () => {
const { queryByTestId } = render(<SectionHeader title="Tokens" />);

expect(queryByTestId(BUTTON_ICON_TEST_ID)).not.toBeOnTheScreen();
});

it('renders trailing icon when onPress is provided', () => {
const { getByTestId } = render(
<SectionHeader title="Tokens" onPress={jest.fn()} />,
);

expect(getByTestId(BUTTON_ICON_TEST_ID)).toBeOnTheScreen();
});

it('has button accessibilityRole when onPress is provided', () => {
const { UNSAFE_getByProps } = render(
<SectionHeader title="Tokens" onPress={jest.fn()} />,
);

expect(
UNSAFE_getByProps({ accessibilityRole: 'button' }),
).toBeOnTheScreen();
});

it('sets the accessibilityLabel to the string title when pressable', () => {
const { UNSAFE_getByProps } = render(
<SectionHeader title="Tokens" onPress={jest.fn()} />,
);

expect(
UNSAFE_getByProps({
accessibilityRole: 'button',
accessibilityLabel: 'Tokens',
}),
).toBeOnTheScreen();
});

it('calls onPress when the header is pressed', () => {
const onPress = jest.fn();
const { getByTestId } = render(
<SectionHeader
title="Tokens"
onPress={onPress}
testID={CONTAINER_TEST_ID}
/>,
);

fireEvent.press(getByTestId(CONTAINER_TEST_ID));

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

it('does not throw when pressed without an onPress prop', () => {
const { getByText } = render(<SectionHeader title="Tokens" />);

expect(() => fireEvent.press(getByText('Tokens'))).not.toThrow();
});
});

describe('endAccessory', () => {
it('renders endAccessory next to the title', () => {
const { getByText } = render(
<SectionHeader title="DeFi" endAccessory={<Text>Info</Text>} />,
);

expect(getByText('DeFi')).toBeOnTheScreen();
expect(getByText('Info')).toBeOnTheScreen();
});

it('renders endAccessory without an onPress', () => {
const { getByText } = render(
<SectionHeader title="DeFi" endAccessory={<Text>Badge</Text>} />,
);

expect(getByText('Badge')).toBeOnTheScreen();
});
});

describe('endIconName', () => {
it('renders the trailing icon when onPress is provided', () => {
const { getByTestId } = render(
<SectionHeader
title="NFTs"
onPress={jest.fn()}
endIconName={IconName.Arrow2Right}
/>,
);

expect(getByTestId(BUTTON_ICON_TEST_ID)).toBeOnTheScreen();
});

it('does not render the trailing icon when onPress is absent', () => {
const { queryByTestId } = render(
<SectionHeader title="NFTs" endIconName={IconName.Arrow2Right} />,
);

expect(queryByTestId(BUTTON_ICON_TEST_ID)).not.toBeOnTheScreen();
});
});

describe('justifyContent', () => {
it('renders title and endAccessory when justifyContent is Between', () => {
const { getByText } = render(
<SectionHeader
title="Your positions"
justifyContent={BoxJustifyContent.Between}
endAccessory={<Text>···</Text>}
/>,
);

expect(getByText('Your positions')).toBeOnTheScreen();
expect(getByText('···')).toBeOnTheScreen();
});
});

describe('endIconColor', () => {
it('renders the trailing icon with the default IconAlternative color', () => {
const { getByTestId } = render(
<SectionHeader title="Tokens" onPress={jest.fn()} />,
);

expect(getByTestId(BUTTON_ICON_TEST_ID)).toBeOnTheScreen();
});

it('renders the trailing icon with a custom color', () => {
const { getByTestId } = render(
<SectionHeader
title="Perps"
onPress={jest.fn()}
endIconColor={IconColor.IconDefault}
/>,
);

expect(getByTestId(BUTTON_ICON_TEST_ID)).toBeOnTheScreen();
});
});

describe('full component', () => {
it('renders title, endAccessory, and trailing icon together', () => {
const onPress = jest.fn();
const { getByText, getByTestId } = render(
<SectionHeader
title="Tokens"
onPress={onPress}
endAccessory={<Text>Badge</Text>}
testID={CONTAINER_TEST_ID}
/>,
);

expect(getByText('Tokens')).toBeOnTheScreen();
expect(getByText('Badge')).toBeOnTheScreen();
expect(getByTestId(BUTTON_ICON_TEST_ID)).toBeOnTheScreen();
expect(getByTestId(CONTAINER_TEST_ID)).toBeOnTheScreen();
});
});
});
Loading
Loading