Skip to content

Commit a586d57

Browse files
committed
feat: add TopAssets & NetworkIcon components
- move AssetLogo from `components` to `product-components` - extract `NetworkIcon` from `CoinLogo` - add `TopAssets` components - if no `contractAddress` and `coingeckoId`, then render `CoinLogo`, else render `AssetLogo`
1 parent c02dcaa commit a586d57

File tree

25 files changed

+437
-146
lines changed

25 files changed

+437
-146
lines changed

packages/components/.storybook/main.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@ module.exports = {
4949
],
5050
});
5151

52+
// Add polyfill for Node.js 'stream' module (required by jws)
53+
config.resolve.fallback = {
54+
...config.resolve.fallback,
55+
stream: require.resolve('stream-browserify'),
56+
};
57+
5258
// NOTE: remove the previous loaders from handling the svgs
5359
const imageRule = config.module.rules.find(rule => rule?.['test']?.test('.svg'));
5460
if (imageRule) {

packages/components/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
"babel-loader": "^10.0.0",
5959
"postcss-styled-syntax": "^0.7.1",
6060
"storybook": "^9.0.16",
61+
"stream-browserify": "^3.0.0",
6162
"stylelint": "^16.14.1",
6263
"stylelint-config-standard": "^38.0.0",
6364
"typescript-styled-plugin": "^0.18.3"

packages/product-components/src/components/AssetLogo/AssetLogo.stories.tsx

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,26 @@ export const AssetLogo: StoryObj<AssetLogoProps> = {
2424
contractAddress: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
2525
shouldTryToFetch: true,
2626
placeholder: 'USDC',
27+
showNetworkIcon: false,
2728
...getFramePropsStory(allowedAssetLogoFrameProps).args,
2829
},
2930
argTypes: {
31+
...getFramePropsStory(allowedAssetLogoFrameProps).argTypes,
3032
size: {
3133
options: allowedAssetLogoSizes,
3234
control: {
3335
type: 'select',
3436
},
3537
},
36-
...getFramePropsStory(allowedAssetLogoFrameProps).argTypes,
38+
showNetworkIcon: {
39+
control: {
40+
type: 'boolean',
41+
},
42+
},
43+
shouldTryToFetch: {
44+
control: {
45+
type: 'boolean',
46+
},
47+
},
3748
},
3849
};

packages/product-components/src/components/AssetLogo/AssetLogo.tsx

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,29 @@ import { useEffect, useMemo, useState } from 'react';
33
import styled from 'styled-components';
44

55
import {
6+
NetworkSymbol,
67
type NetworkSymbolExtended,
78
getNetwork,
89
getNetworkByCoingeckoId,
10+
getNetworkFeatures,
911
isNetworkSymbol,
1012
} from '@suite-common/wallet-config';
1113
import { getAssetLogoContractAddresses } from '@suite-common/wallet-utils';
1214
import { getAssetLogoUrl } from '@trezor/asset-utils';
1315
import {
1416
ElevationUp,
15-
type FrameProps,
16-
type FramePropsKeys,
17-
type TransientProps,
17+
FrameProps,
18+
FramePropsKeys,
19+
TransientProps,
1820
pickAndPrepareFrameProps,
1921
useElevation,
2022
withFrameProps,
2123
} from '@trezor/components';
2224
import { Elevation, borders, mapElevationToBackground, mapElevationToBorder } from '@trezor/theme';
2325

2426
import { AssetInitials } from './AssetInitials';
27+
import { LegacyNetworkSymbol, isNetworkSymbolWithIcon } from '../../constants/networks';
28+
import { NetworkIcon } from '../NetworkIcon/NetworkIcon';
2529

2630
export const allowedAssetLogoSizes = [20, 24, 32, 40] as const;
2731
type AssetLogoSize = (typeof allowedAssetLogoSizes)[number];
@@ -38,6 +42,8 @@ export type AssetLogoProps = AllowedFrameProps & {
3842
placeholderWithTooltip?: boolean;
3943
placeholder: string;
4044
'data-testid'?: string;
45+
showNetworkIcon?: boolean;
46+
className?: string;
4147
};
4248

4349
type LogoCandidate = { address: string; src: string; srcSet: string };
@@ -46,6 +52,7 @@ const Container = styled.div<TransientProps<AllowedFrameProps> & { $size: number
4652
${({ $size }) => `
4753
width: ${$size}px;
4854
height: ${$size}px;
55+
position: relative;
4956
`}
5057
${withFrameProps}
5158
`;
@@ -58,6 +65,17 @@ const Logo = styled.img<{ $size: number; $elevation: Elevation }>`
5865
background-color: ${mapElevationToBackground};
5966
`;
6067

68+
const StyledNetworkIcon = styled(NetworkIcon)`
69+
position: absolute;
70+
bottom: 0;
71+
right: 0;
72+
line-height: 0;
73+
74+
div {
75+
line-height: 0;
76+
}
77+
`;
78+
6179
interface LogoProps extends React.ImgHTMLAttributes<HTMLImageElement> {
6280
$size: number;
6381
}
@@ -78,6 +96,19 @@ const makeCacheKey = (coingeckoId: string, addressesKey: string) =>
7896

7997
const makeAddressKey = (coingeckoId: string, address: string) => `${coingeckoId}::${address}`;
8098

99+
export function shouldShowNetworkIcon(
100+
networkSymbol?: NetworkSymbolExtended,
101+
contractAddress?: string | null,
102+
) {
103+
return (
104+
networkSymbol &&
105+
isNetworkSymbolWithIcon(networkSymbol) &&
106+
isNetworkSymbol(networkSymbol) &&
107+
Boolean(contractAddress) &&
108+
getNetworkFeatures(networkSymbol).includes('tokens')
109+
);
110+
}
111+
81112
export const getCoingeckoIdAndContractAddressIncludesNativeTokens = (
82113
coingeckoId: string,
83114
contractAddress: string[] | undefined,
@@ -106,11 +137,13 @@ export const AssetLogo = ({
106137
size,
107138
coingeckoId,
108139
symbol,
109-
contractAddress,
140+
contractAddress = null,
110141
shouldTryToFetch = true,
111142
placeholder,
112143
placeholderWithTooltip = true,
144+
showNetworkIcon = false,
113145
'data-testid': dataTest,
146+
className,
114147
...rest
115148
}: AssetLogoProps) => {
116149
const contractAddressArray = useMemo(
@@ -219,7 +252,7 @@ export const AssetLogo = ({
219252
};
220253

221254
return (
222-
<Container $size={size} {...frameProps}>
255+
<Container $size={size} {...frameProps} className={className}>
223256
{showPlaceholder && (
224257
<AssetInitials size={size} withTooltip={placeholderWithTooltip}>
225258
{placeholder}
@@ -238,6 +271,12 @@ export const AssetLogo = ({
238271
onLoad={handleOnLoad}
239272
onError={handleLoadError}
240273
/>
274+
{showNetworkIcon && (
275+
<StyledNetworkIcon
276+
networkSymbol={symbol as NetworkSymbol | LegacyNetworkSymbol}
277+
size={size * 0.375}
278+
/>
279+
)}
241280
</ElevationUp>
242281
)}
243282
</Container>

packages/product-components/src/components/CoinLogo/CoinLogo.stories.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import { Meta, StoryObj } from '@storybook/react';
22
import styled from 'styled-components';
33

4-
import { COIN_LOGO_TYPE, CoinLogoProps } from './CoinLogo';
5-
import { COINS } from './coins';
6-
import { CoinLogo as CoinLogoComponent } from '../../index';
4+
import { COIN_LOGO_TYPE, CoinLogo as CoinLogoComponent, CoinLogoProps } from './CoinLogo';
5+
import { COINS } from '../../constants/coins';
76

87
const Center = styled.div`
98
display: flex;

packages/product-components/src/components/CoinLogo/CoinLogo.tsx

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,19 @@ import styled from 'styled-components';
55

66
import { NetworkSymbol, getNetworkOptional } from '@suite-common/wallet-config';
77
import { borders } from '@trezor/theme';
8+
import { roundTo } from '@trezor/utils';
89

9-
import { COINS, LegacyNetworkSymbol } from './coins';
10-
import { NETWORK_ICONS } from './networks';
10+
import { COINS, LegacyNetworkSymbol } from '../../constants/coins';
11+
import { NETWORK_ICONS } from '../../constants/networks';
12+
import { NetworkIcon } from '../NetworkIcon/NetworkIcon';
1113

1214
export const COIN_LOGO_TYPE = ['token', 'network', 'tokenWithNetwork'] as const;
1315
export type CoinLogoType = (typeof COIN_LOGO_TYPE)[number];
1416

1517
const DEFAULT_SIZE = 32;
1618

17-
const getSize = (size?: number, border = 0, divisor = 1) =>
18-
`${(size ?? DEFAULT_SIZE) / divisor + border * 2}px`;
19+
const getSize = (size: number = DEFAULT_SIZE, border = 0, divisor = 1) =>
20+
`${roundTo(size / divisor + border * 2, 2)}px`;
1921

2022
export interface CoinLogoProps extends ImgHTMLAttributes<HTMLImageElement> {
2123
symbol: NetworkSymbol | LegacyNetworkSymbol;
@@ -98,16 +100,7 @@ export const CoinLogo = ({
98100
}}
99101
loading={() => <span className="loading" />}
100102
/>
101-
{badge && symbolSrc != null && (
102-
<ReactSVG
103-
src={badge}
104-
beforeInjection={svg => {
105-
svg.setAttribute('width', getSize(size, 0, 3));
106-
svg.setAttribute('height', getSize(size, 0, 3));
107-
}}
108-
loading={() => <span className="loading" />}
109-
/>
110-
)}
103+
{badge && <NetworkIcon networkSymbol={symbol} size={roundTo(size / 3, 2)} />}
111104
</SvgWrapper>
112105
);
113106
};

packages/product-components/src/components/CoinLogo/coinLogos.stories.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ import styled from 'styled-components';
44
import { networksCollection } from '@suite-common/wallet-config';
55
import { StoryColumn } from '@trezor/components';
66

7-
import { COINS, isCoinSymbol } from './coins';
8-
import { NETWORK_ICONS, isNetworkSymbol } from './networks';
9-
import { CoinLogo } from '../../index';
7+
import { CoinLogo } from './CoinLogo';
8+
import { COINS, isCoinSymbol } from '../../constants/coins';
9+
import { NETWORK_ICONS, isNetworkSymbolWithIcon } from '../../constants/networks';
1010

1111
const Heading = styled.h2`
1212
margin-bottom: 2px;
@@ -67,7 +67,7 @@ export const All: StoryObj = {
6767
{Object.keys(NETWORK_ICONS).map(network => (
6868
<Icon key={network}>
6969
<CoinName>{network}</CoinName>
70-
{isNetworkSymbol(network) && (
70+
{isNetworkSymbolWithIcon(network) && (
7171
<CoinLogo
7272
symbol={network}
7373
data-testid={`network-${network}`}

packages/product-components/src/components/CoinLogo/coins.ts

Lines changed: 0 additions & 55 deletions
This file was deleted.

packages/product-components/src/components/CoinLogo/networks.ts

Lines changed: 0 additions & 45 deletions
This file was deleted.

0 commit comments

Comments
 (0)