Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UX: Multichain: Added TokenList Component #17859

Merged
merged 56 commits into from
Mar 23, 2023
Merged
Show file tree
Hide file tree
Changes from 55 commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
9e13d6b
added redesign storybook
NidhiKJha Feb 22, 2023
a32400d
updated token list
NidhiKJha Feb 27, 2023
44fa929
updated css
NidhiKJha Feb 27, 2023
9a536fe
fixed lint error
NidhiKJha Feb 27, 2023
c6ac4a2
updated the new token list component
NidhiKJha Mar 1, 2023
94d47cb
fixed redesign folder error
NidhiKJha Mar 1, 2023
507f0dc
reverted changes in settings.json
NidhiKJha Mar 1, 2023
4580cdd
updated redesign to multichain
NidhiKJha Mar 1, 2023
a7b0a6e
added feature flag
NidhiKJha Mar 1, 2023
70c0e33
reverted settings.json
NidhiKJha Mar 1, 2023
657c585
added detect token banner
NidhiKJha Mar 2, 2023
f7d3610
added button componeny
NidhiKJha Mar 2, 2023
cc9ef62
fixed lint errors
NidhiKJha Mar 2, 2023
49e1dca
removed settings
NidhiKJha Mar 2, 2023
6d3f408
fixed lint errors
NidhiKJha Mar 2, 2023
822f589
added stories for multichain
NidhiKJha Mar 2, 2023
f7aa429
updated no token found string
NidhiKJha Mar 2, 2023
1d89851
updated lint error
NidhiKJha Mar 2, 2023
ae75f7a
updated padding values
NidhiKJha Mar 2, 2023
f1ff4c7
updated padding values
NidhiKJha Mar 2, 2023
75bb4c6
updated tabs with role button
NidhiKJha Mar 3, 2023
a0db571
updated hover state
NidhiKJha Mar 6, 2023
00a5581
updated components with multichain
NidhiKJha Mar 7, 2023
a2fef4d
fixed lint errors
NidhiKJha Mar 7, 2023
776f01c
updated multichain import token link
NidhiKJha Mar 7, 2023
e69894b
updated a tag
NidhiKJha Mar 8, 2023
a6727f8
updated fixes
NidhiKJha Mar 9, 2023
2a2d079
updated onClick to handleClick
NidhiKJha Mar 9, 2023
7ac8e63
updated setShowDetectedTokens proptype
NidhiKJha Mar 9, 2023
96073fc
updated multichain tokenlist with item suffix
NidhiKJha Mar 9, 2023
f94fdce
updated tests
NidhiKJha Mar 9, 2023
0f3c600
updated tests
NidhiKJha Mar 9, 2023
bcaa29d
updated token list css
NidhiKJha Mar 10, 2023
8806e55
updated snapshot
NidhiKJha Mar 10, 2023
68a758f
updated text
NidhiKJha Mar 13, 2023
ec5f4e6
reverted story
NidhiKJha Mar 13, 2023
a4f45c9
added story for multichain token list
NidhiKJha Mar 13, 2023
d106c30
updated story
NidhiKJha Mar 14, 2023
0b435f3
updated tooltip
NidhiKJha Mar 14, 2023
b298141
updated the new token list component
NidhiKJha Mar 1, 2023
cf0b5bc
fixed redesign folder error
NidhiKJha Mar 1, 2023
6bd40b1
added feature flag
NidhiKJha Mar 1, 2023
5539d3e
reverted unused setting change
NidhiKJha Mar 14, 2023
64764c0
removed token list
NidhiKJha Mar 14, 2023
0057e47
fixed lint error
NidhiKJha Mar 15, 2023
cb087cb
updated status
NidhiKJha Mar 15, 2023
f4cb5e1
updated tooltip
NidhiKJha Mar 15, 2023
0b66049
updated token-list-item changes
NidhiKJha Mar 16, 2023
cd32aa1
updated actionbutton click for detect token banner
NidhiKJha Mar 16, 2023
14b5a09
updated snapshot
NidhiKJha Mar 16, 2023
7129d1d
updated symbol
NidhiKJha Mar 16, 2023
77ed5fa
updated styles
NidhiKJha Mar 20, 2023
2192677
updated eth decimal and token url
NidhiKJha Mar 20, 2023
407f92f
updated snapshot
NidhiKJha Mar 20, 2023
c0bb166
updated scripts
NidhiKJha Mar 22, 2023
79102f1
updated snapshots
NidhiKJha Mar 22, 2023
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
94 changes: 67 additions & 27 deletions ui/components/app/asset-list/asset-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,26 @@ import {
getNativeCurrencyImage,
getDetectedTokensInCurrentNetwork,
getIstokenDetectionInactiveOnNonMainnetSupportedNetwork,
getTokenList,
} from '../../../selectors';
import { getNativeCurrency } from '../../../ducks/metamask/metamask';
import { useCurrencyDisplay } from '../../../hooks/useCurrencyDisplay';
import Typography from '../../ui/typography/typography';
import Box from '../../ui/box/box';
import {
Color,
TypographyVariant,
FONT_WEIGHT,
JustifyContent,
TextVariant,
TEXT_ALIGN,
} from '../../../helpers/constants/design-system';
import { useI18nContext } from '../../../hooks/useI18nContext';
import { MetaMetricsContext } from '../../../contexts/metametrics';
import { EVENT, EVENT_NAMES } from '../../../../shared/constants/metametrics';
import DetectedToken from '../detected-token/detected-token';
import {
DetectedTokensBanner,
MultichainTokenListItem,
MultichainImportTokenLink,
} from '../../multichain';
import { Text } from '../../component-library';
import DetectedTokensLink from './detetcted-tokens-link/detected-tokens-link';

const AssetList = ({ onClickAsset }) => {
Expand Down Expand Up @@ -69,20 +74,38 @@ const AssetList = ({ onClickAsset }) => {
const istokenDetectionInactiveOnNonMainnetSupportedNetwork = useSelector(
getIstokenDetectionInactiveOnNonMainnetSupportedNetwork,
);

const tokenList = useSelector(getTokenList);
const tokenData = Object.values(tokenList).find(
(token) => token.symbol === primaryCurrencyProperties.suffix,
);
const title = tokenData?.name || primaryCurrencyProperties.suffix;
return (
<>
<AssetListItem
onClick={() => onClickAsset(nativeCurrency)}
data-testid="wallet-balance"
primary={
primaryCurrencyProperties.value ?? secondaryCurrencyProperties.value
}
tokenSymbol={primaryCurrencyProperties.suffix}
secondary={showFiat ? secondaryCurrencyDisplay : undefined}
tokenImage={balanceIsLoading ? null : primaryTokenImage}
identiconBorder
/>
{process.env.MULTICHAIN ? (
<MultichainTokenListItem
NidhiKJha marked this conversation as resolved.
Show resolved Hide resolved
onClick={() => onClickAsset(nativeCurrency)}
title={title}
primary={
primaryCurrencyProperties.value ?? secondaryCurrencyProperties.value
}
tokenSymbol={primaryCurrencyProperties.suffix}
secondary={showFiat ? secondaryCurrencyDisplay : undefined}
tokenImage={balanceIsLoading ? null : primaryTokenImage}
/>
) : (
<AssetListItem
onClick={() => onClickAsset(nativeCurrency)}
data-testid="wallet-balance"
primary={
primaryCurrencyProperties.value ?? secondaryCurrencyProperties.value
}
tokenSymbol={primaryCurrencyProperties.suffix}
secondary={showFiat ? secondaryCurrencyDisplay : undefined}
tokenImage={balanceIsLoading ? null : primaryTokenImage}
identiconBorder
/>
)}

<TokenList
onTokenClick={(tokenAddress) => {
onClickAsset(tokenAddress);
Expand All @@ -98,19 +121,36 @@ const AssetList = ({ onClickAsset }) => {
/>
{detectedTokens.length > 0 &&
!istokenDetectionInactiveOnNonMainnetSupportedNetwork && (
<DetectedTokensLink setShowDetectedTokens={setShowDetectedTokens} />
<>
{process.env.MULTICHAIN ? (
<DetectedTokensBanner
actionButtonOnClick={() => setShowDetectedTokens(true)}
margin={4}
/>
) : (
<DetectedTokensLink
setShowDetectedTokens={setShowDetectedTokens}
/>
)}
</>
)}
<Box marginTop={detectedTokens.length > 0 ? 0 : 4}>
<Box justifyContent={JustifyContent.center}>
<Typography
color={Color.textAlternative}
variant={TypographyVariant.H6}
fontWeight={FONT_WEIGHT.NORMAL}
>
{t('missingToken')}
</Typography>
</Box>
<ImportTokenLink />
{process.env.MULTICHAIN ? (
<MultichainImportTokenLink margin={4} />
) : (
<>
<Text
color={Color.textAlternative}
variant={TextVariant.bodySm}
as="h6"
textAlign={TEXT_ALIGN.CENTER}
>
{t('missingToken')}
</Text>

<ImportTokenLink />
</>
)}
</Box>
{showDetectedTokens && (
<DetectedToken setShowDetectedTokens={setShowDetectedTokens} />
georgewrmarshall marked this conversation as resolved.
Show resolved Hide resolved
Expand Down
70 changes: 46 additions & 24 deletions ui/components/app/token-cell/token-cell.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@ import PropTypes from 'prop-types';
import React from 'react';
import { useSelector } from 'react-redux';
import AssetListItem from '../asset-list-item';
import { getSelectedAddress } from '../../../selectors';
import { getSelectedAddress, getTokenList } from '../../../selectors';
import { useI18nContext } from '../../../hooks/useI18nContext';
import { useTokenFiatAmount } from '../../../hooks/useTokenFiatAmount';
import { MultichainTokenListItem } from '../../multichain';
import { ButtonLink, Text } from '../../component-library';
import { TextColor } from '../../../helpers/constants/design-system';

export default function TokenCell({
address,
Expand All @@ -19,39 +22,58 @@ export default function TokenCell({
}) {
const userAddress = useSelector(getSelectedAddress);
const t = useI18nContext();

const tokenList = useSelector(getTokenList);
const tokenData = Object.values(tokenList).find(
(token) => token.symbol === symbol,
);
const title = tokenData?.name || symbol;
const tokenImage = tokenData?.iconUrl || image;
const formattedFiat = useTokenFiatAmount(address, string, symbol);
const warning = balanceError ? (
NidhiKJha marked this conversation as resolved.
Show resolved Hide resolved
<span>
<Text as="span">
{t('troubleTokenBalances')}
<a
<ButtonLink
href={`https://ethplorer.io/address/${userAddress}`}
rel="noopener noreferrer"
target="_blank"
externalLink
onClick={(event) => event.stopPropagation()}
style={{ color: 'var(--color-warning-default)' }}
textProps={{
color: TextColor.warningDefault,
}}
>
{t('here')}
</a>
</span>
</ButtonLink>
</Text>
) : null;

return (
<AssetListItem
className={classnames('token-cell', {
'token-cell--outdated': Boolean(balanceError),
})}
iconClassName="token-cell__icon"
onClick={onClick.bind(null, address)}
tokenAddress={address}
tokenSymbol={symbol}
tokenDecimals={decimals}
tokenImage={image}
warning={warning}
primary={`${string || 0}`}
secondary={formattedFiat}
isERC721={isERC721}
/>
<>
{process.env.MULTICHAIN ? (
<MultichainTokenListItem
onClick={() => onClick(address)}
tokenSymbol={symbol}
tokenImage={tokenImage}
primary={`${string || 0}`}
secondary={formattedFiat}
title={title}
/>
) : (
<AssetListItem
className={classnames('token-cell', {
'token-cell--outdated': Boolean(balanceError),
})}
iconClassName="token-cell__icon"
onClick={() => onClick(address)}
tokenAddress={address}
tokenSymbol={symbol}
tokenDecimals={decimals}
tokenImage={image}
warning={warning}
primary={`${string || 0}`}
secondary={formattedFiat}
isERC721={isERC721}
/>
)}
</>
);
}

NidhiKJha marked this conversation as resolved.
Show resolved Hide resolved
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`DetectedTokensBanner should render correctly 1`] = `
<div>
<div
class="box mm-banner-base mm-banner-alert mm-banner-alert--severity-info multichain-detected-token-banner box--padding-3 box--padding-left-2 box--display-flex box--gap-2 box--flex-direction-row box--background-color-primary-muted box--rounded-sm"
data-testid="detected-token-banner"
>
<span
class="box mm-icon mm-icon--size-lg box--display-inline-block box--flex-direction-row box--color-primary-default"
style="mask-image: url('./images/icons/info.svg');"
/>
<div>
<p
class="box mm-text mm-text--body-md mm-text--color-text-default box--flex-direction-row"
>
3 new tokens found in this account
</p>
<button
class="box mm-button-base mm-button-link mm-button-link--size-auto box--display-inline-flex box--flex-direction-row box--justify-content-center box--align-items-center box--color-primary-default box--background-color-transparent"
>
<span
class="box mm-text mm-button-base__content mm-text--body-md mm-text--color-inherit box--gap-2 box--flex-direction-row box--justify-content-center box--align-items-center box--display-flex"
>
Import tokens
</span>
</button>
</div>
</div>
</div>
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import React, { useContext } from 'react';
import { useSelector } from 'react-redux';
import PropTypes from 'prop-types';
import classNames from 'classnames';

import { useI18nContext } from '../../../hooks/useI18nContext';
import { getDetectedTokensInCurrentNetwork } from '../../../selectors';
import { MetaMetricsContext } from '../../../contexts/metametrics';
import { EVENT, EVENT_NAMES } from '../../../../shared/constants/metametrics';
import { BannerAlert } from '../../component-library';

export const DetectedTokensBanner = ({
className,
actionButtonOnClick,
...props
}) => {
const t = useI18nContext();
const trackEvent = useContext(MetaMetricsContext);

const detectedTokens = useSelector(getDetectedTokensInCurrentNetwork);
const detectedTokensDetails = detectedTokens.map(
({ address, symbol }) => `${symbol} - ${address}`,
);

const handleOnClick = () => {
actionButtonOnClick();
trackEvent({
event: EVENT_NAMES.TOKEN_IMPORT_CLICKED,
category: EVENT.CATEGORIES.WALLET,
properties: {
source: EVENT.SOURCE.TOKEN.DETECTED,
tokens: detectedTokensDetails,
},
});
};
return (
<BannerAlert
className={classNames('multichain-detected-token-banner', className)}
actionButtonLabel={t('importTokensCamelCase')}
actionButtonOnClick={handleOnClick}
data-testid="detected-token-banner"
{...props}
>
{detectedTokens.length === 1
? t('numberOfNewTokensDetectedSingular')
: t('numberOfNewTokensDetectedPlural', [detectedTokens.length])}
</BannerAlert>
);
};

DetectedTokensBanner.propTypes = {
/**
* Handler to be passed to the DetectedTokenBanner component
*/
actionButtonOnClick: PropTypes.func.isRequired,
/**
* An additional className to the DetectedTokenBanner component
*/
className: PropTypes.string,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React from 'react';
import { DetectedTokensBanner } from './detected-token-banner';

export default {
title: 'Components/Multichain/DetectedTokensBanner',
component: DetectedTokensBanner,
argTypes: {
actionButtonOnClick: { action: 'setShowDetectedTokens' },
},
};

export const DefaultStory = (args) => <DetectedTokensBanner {...args} />;

DefaultStory.storyName = 'Default';
NidhiKJha marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React from 'react';
import { renderWithProvider, screen, fireEvent } from '../../../../test/jest';
import configureStore from '../../../store/store';
import testData from '../../../../.storybook/test-data';

import { DetectedTokensBanner } from './detected-token-banner';

describe('DetectedTokensBanner', () => {
let setShowDetectedTokensSpy;

const args = {};

beforeEach(() => {
setShowDetectedTokensSpy = jest.fn();
args.actionButtonOnClick = setShowDetectedTokensSpy;
});

it('should render correctly', () => {
const store = configureStore(testData);
const { getByTestId, container } = renderWithProvider(
<DetectedTokensBanner {...args} />,
store,
);

expect(getByTestId('detected-token-banner')).toBeDefined();
expect(container).toMatchSnapshot();
});
it('should render number of tokens detected link', () => {
const store = configureStore(testData);
renderWithProvider(<DetectedTokensBanner {...args} />, store);

expect(
screen.getByText('3 new tokens found in this account'),
).toBeInTheDocument();

fireEvent.click(screen.getByText('Import tokens'));
expect(setShowDetectedTokensSpy).toHaveBeenCalled();
});
});
NidhiKJha marked this conversation as resolved.
Show resolved Hide resolved
1 change: 1 addition & 0 deletions ui/components/multichain/detected-token-banner/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { DetectedTokensBanner } from './detected-token-banner';
3 changes: 3 additions & 0 deletions ui/components/multichain/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
export { AccountListItem } from './account-list-item';
export { AccountListItemMenu } from './account-list-item-menu';
export { AccountListMenu } from './account-list-menu';
export { DetectedTokensBanner } from './detected-token-banner';
export { MultichainImportTokenLink } from './multichain-import-token-link';
export { MultichainTokenListItem } from './multichain-token-list-item';
2 changes: 0 additions & 2 deletions ui/components/multichain/index.scss

This file was deleted.

9 changes: 9 additions & 0 deletions ui/components/multichain/multichain-components.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* Please import your styles in order of atomicity.
* The most atomic styles should be imported first.
* This will help improve specificity and reduce the chance of
* unintended overrides.
**/
@import 'account-list-item/index';
@import 'account-list-menu/index';
@import 'multichain-token-list-item/multichain-token-list-item';
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@darkwing I have removed index.scss and replaced it with multichain-components.scss to keep in sync with how we have named the default global scss files in component-library, app and ui folder.

Loading