Skip to content

Commit

Permalink
feat: built generic TokenListItem component
Browse files Browse the repository at this point in the history
  • Loading branch information
Matt561 committed Jan 23, 2025
1 parent afbd917 commit 931ed4e
Show file tree
Hide file tree
Showing 5 changed files with 393 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { StyleSheet } from 'react-native';

const styleSheet = () =>
StyleSheet.create({
container: {
padding: 16,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
},
left: {
flexDirection: 'row',
alignItems: 'center',
gap: 16,
},
right: {
alignItems: 'flex-end',
justifyContent: 'center',
},
});

export default styleSheet;
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import React from 'react';
import { TokenListItemProps } from './TokenListItem.types';
import TokenListItem from '.';
import { strings } from '../../../../../../locales/i18n';
import renderWithProvider from '../../../../../util/test/renderWithProvider';
import { useSelector } from 'react-redux';
import { selectIsIpfsGatewayEnabled } from '../../../../../selectors/preferencesController';
import {
TextColor,
TextVariant,
} from '../../../../../component-library/components/Texts/Text';

jest.mock('react-redux', () => ({
...jest.requireActual('react-redux'),
useSelector: jest.fn(),
}));

describe('TokenListItem', () => {
beforeEach(() => {
(useSelector as jest.Mock).mockImplementation((selector) => {
if (selector === selectIsIpfsGatewayEnabled) return true;
});
});

afterEach(() => {
(useSelector as jest.Mock).mockClear();
});

const baseProps: TokenListItemProps = {
token: {
chainId: '0x1',
image:
'https://static.cx.metamask.io/api/v1/tokenIcons/1/0x6b175474e89094c44da98b954eedeac495271d0f.png',
name: 'Dai Stablecoin',
symbol: 'DAI',
},
primaryText: {
value: `3.0% ${strings('stake.apr')}`,
variant: TextVariant.BodyMDBold,
color: TextColor.Success,
},
onPress: jest.fn(),
};

const secondaryText = {
value: '10,100.00 USDC',
variant: TextVariant.BodySMBold,
color: TextColor.Alternative,
};

it('render matches snapshot', () => {
const props: TokenListItemProps = {
...baseProps,
secondaryText,
};

const { toJSON } = renderWithProvider(<TokenListItem {...props} />);

expect(toJSON()).toMatchSnapshot();
});

it('renders primary text and secondary text', () => {
const props: TokenListItemProps = {
...baseProps,
secondaryText,
};

const { getByText } = renderWithProvider(<TokenListItem {...props} />);

expect(getByText('Dai Stablecoin')).toBeDefined();
expect(getByText('10,100.00 USDC')).toBeDefined();
});

it('renders only primary text', () => {
const { getByText } = renderWithProvider(<TokenListItem {...baseProps} />);

expect(getByText('Dai Stablecoin')).toBeDefined();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { TextProps } from '../../../../../component-library/components/Texts/Text/Text.types';

interface Text extends Omit<TextProps, 'children'> {
value: string;
}

export interface TokenListAsset {
name: string;
symbol: string;
chainId: string;
image: string;
}

export interface TokenListItemProps {
token: TokenListAsset;
primaryText: Text;
secondaryText?: Text;
onPress: (token: TokenListAsset) => void;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`TokenListItem render matches snapshot 1`] = `
<TouchableOpacity
onPress={[Function]}
style={
{
"alignItems": "center",
"flexDirection": "row",
"justifyContent": "space-between",
"padding": 16,
}
}
>
<View
style={
{
"alignItems": "center",
"flexDirection": "row",
"gap": 16,
}
}
>
<View
onLayout={[Function]}
style={
{
"alignSelf": "flex-start",
"position": "relative",
}
}
testID="badge-wrapper-badge"
>
<View>
<View
style={
{
"backgroundColor": "#ffffff",
"borderRadius": 16,
"height": 32,
"overflow": "hidden",
"width": 32,
}
}
>
<Image
onError={[Function]}
resizeMode="contain"
source={
{
"uri": "https://static.cx.metamask.io/api/v1/tokenIcons/1/0x6b175474e89094c44da98b954eedeac495271d0f.png",
}
}
style={
{
"flex": 1,
"height": undefined,
"width": undefined,
}
}
testID="token-avatar-image"
/>
</View>
</View>
<View
style={
{
"alignItems": "center",
"aspectRatio": 1,
"height": 0,
"justifyContent": "center",
"position": "absolute",
"right": 0,
"top": 0,
"transform": [
{
"translateX": 0,
},
{
"translateY": -0,
},
],
}
}
>
<View
onLayout={[Function]}
style={
{
"alignItems": "center",
"aspectRatio": 1,
"height": "50%",
"justifyContent": "center",
"maxHeight": 24,
"minHeight": 8,
"opacity": 0,
}
}
testID="badgenetwork"
>
<View
style={
{
"alignItems": "center",
"backgroundColor": "#ffffff",
"borderColor": "#ffffff",
"borderRadius": 16,
"borderWidth": 2,
"height": 32,
"justifyContent": "center",
"overflow": "hidden",
"shadowColor": "#0000001a",
"shadowOffset": {
"height": 2,
"width": 0,
},
"shadowOpacity": 1,
"shadowRadius": 4,
"transform": [
{
"scale": 1,
},
],
"width": 32,
}
}
>
<Image
onError={[Function]}
resizeMode="contain"
source={1}
style={
{
"height": 32,
"width": 32,
}
}
testID="network-avatar-image"
/>
</View>
</View>
</View>
</View>
<Text
accessibilityRole="text"
style={
{
"color": "#141618",
"fontFamily": "EuclidCircularB-Medium",
"fontSize": 14,
"fontWeight": "500",
"letterSpacing": 0,
"lineHeight": 22,
}
}
>
Dai Stablecoin
</Text>
</View>
<View
style={
{
"alignItems": "flex-end",
"justifyContent": "center",
}
}
>
<Text
accessibilityRole="text"
style={
{
"color": "#1c8234",
"fontFamily": "EuclidCircularB-Bold",
"fontSize": 14,
"fontWeight": "700",
"letterSpacing": 0,
"lineHeight": 22,
}
}
>
3.0% APR
</Text>
<Text
accessibilityRole="text"
style={
{
"color": "#6a737d",
"fontFamily": "EuclidCircularB-Bold",
"fontSize": 12,
"fontWeight": "700",
"letterSpacing": 0,
"lineHeight": 20,
}
}
>
10,100.00 USDC
</Text>
</View>
</TouchableOpacity>
`;
73 changes: 73 additions & 0 deletions app/components/UI/Stake/components/TokenListItem/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import React from 'react';
import { TouchableOpacity, View } from 'react-native';
import { useSelector } from 'react-redux';
import { AvatarSize } from '../../../../../component-library/components/Avatars/Avatar';
import AvatarToken from '../../../../../component-library/components/Avatars/Avatar/variants/AvatarToken';
import Badge, {
BadgeVariant,
} from '../../../../../component-library/components/Badges/Badge';
import BadgeWrapper from '../../../../../component-library/components/Badges/BadgeWrapper';
import Text, {
TextColor,
TextVariant,
} from '../../../../../component-library/components/Texts/Text';
import { selectNetworkName } from '../../../../../selectors/networkInfos';
import { useStyles } from '../../../../hooks/useStyles';
import { TokenListItemProps } from './TokenListItem.types';
import { getNetworkImageSource } from '../../../../../util/networks';
import styleSheet from './TokenListItem.styles';

const TokenListItem = ({
token,
primaryText,
secondaryText,
onPress,
}: TokenListItemProps) => {
const { styles } = useStyles(styleSheet, {});

const networkName = useSelector(selectNetworkName);

return (
<TouchableOpacity style={styles.container} onPress={() => onPress(token)}>
<View style={styles.left}>
<BadgeWrapper
badgeElement={
<Badge
variant={BadgeVariant.Network}
name={networkName}
// @ts-expect-error The utils/network file is still JS and this function expects a networkType, and should be optional
imageSource={getNetworkImageSource({ chainId: token.chainId })}
/>
}
>
<AvatarToken
name={token.symbol}
imageSource={{ uri: token.image }}
size={AvatarSize.Md}
/>
</BadgeWrapper>
<Text variant={TextVariant.BodyMDMedium}>{token.name}</Text>
</View>
<View style={styles.right}>
{primaryText.value && (
<Text
variant={primaryText?.variant ?? TextVariant.BodyMDMedium}
color={primaryText?.color}
>
{primaryText.value}
</Text>
)}
{secondaryText?.value && (
<Text
variant={secondaryText?.variant ?? TextVariant.BodySMMedium}
color={secondaryText?.color ?? TextColor.Alternative}
>
{secondaryText.value}
</Text>
)}
</View>
</TouchableOpacity>
);
};

export default TokenListItem;

0 comments on commit 931ed4e

Please sign in to comment.