1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-10-22 11:22:43 +02:00

Redesign screen/page "List of networks" (#13560)

Co-authored-by: George Marshall <george.marshall@consensys.net>
Co-authored-by: Elliot Winkler <elliot.winkler@gmail.com>
This commit is contained in:
VSaric 2022-03-31 16:41:39 +02:00 committed by GitHub
parent 8174aaa6b5
commit 8184b150cb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 598 additions and 40 deletions

View File

@ -13,6 +13,103 @@ const state = {
protocol: 'https:',
url: 'https://metamask.github.io/test-dapp/',
},
networkList: [
{
blockExplorerUrl: "https://etherscan.io",
chainId: "0x1",
iconColor: 'var(--mainnet)',
isATestNetwork: false,
labelKey: "mainnet",
providerType: "mainnet",
rpcUrl: "https://mainnet.infura.io/v3/",
ticker: "ETH",
viewOnly: true,
},
{
blockExplorerUrl: "https://ropsten.etherscan.io",
chainId: "0x3",
iconColor: 'var(--ropsten)',
isATestNetwork: true,
labelKey: "ropsten",
providerType: "ropsten",
rpcUrl: "https://ropsten.infura.io/v3/",
ticker: "ETH",
viewOnly: true,
},
{
blockExplorerUrl: "https://rinkeby.etherscan.io",
chainId: "0x4",
iconColor: 'var(--rinkeby)',
isATestNetwork: true,
labelKey: "rinkeby",
providerType: "rinkeby",
rpcUrl: "https://rinkeby.infura.io/v3/",
ticker: "ETH",
viewOnly: true,
},
{
blockExplorerUrl: "https://goerli.etherscan.io",
chainId: "0x5",
iconColor: 'var(--goerli)',
isATestNetwork: true,
labelKey: "goerli",
providerType: "goerli",
rpcUrl: "https://goerli.infura.io/v3/",
ticker: "ETH",
viewOnly: true,
},
{
blockExplorerUrl: "https://kovan.etherscan.io",
chainId: "0x2a",
iconColor: 'var(--kovan)',
isATestNetwork: true,
labelKey: "kovan",
providerType: "kovan",
rpcUrl: "https://kovan.infura.io/v3/",
ticker: "ETH",
viewOnly: true,
},
{
blockExplorerUrl: "",
chainId: "0x539",
iconColor: 'var(--localhost)',
isATestNetwork: true,
label: "Localhost 8545",
providerType: "rpc",
rpcUrl: "http://localhost:8545",
ticker: "ETH",
},
{
blockExplorerUrl: "https://bscscan.com",
chainId: "0x38",
iconColor: 'var(--localhost)',
isATestNetwork: false,
label: "Binance Smart Chain",
providerType: "rpc",
rpcUrl: "https://bsc-dataseed.binance.org/",
ticker: "BNB",
},
{
blockExplorerUrl: "https://cchain.explorer.avax.network/",
chainId: "0xa86a",
iconColor: 'var(--localhost)',
isATestNetwork: false,
label: "Avalanche",
providerType: "rpc",
rpcUrl: "https://api.avax.network/ext/bc/C/rpc",
ticker: "AVAX",
},
{
blockExplorerUrl: "https://polygonscan.com",
chainId: "0x89",
iconColor: 'var(--localhost)',
isATestNetwork: false,
label: "Polygon",
providerType: "rpc",
rpcUrl: "https://polygon-rpc.com",
ticker: "MATIC",
},
],
metamask: {
tokenList: {
'0x6b175474e89094c44da98b954eedeac495271d0f': {

View File

@ -714,6 +714,9 @@
"custom": {
"message": "Advanced"
},
"customContentSearch": {
"message": "Search for a previously added network"
},
"customGas": {
"message": "Customize Gas"
},
@ -2798,7 +2801,7 @@
"message": "Settings"
},
"settingsSearchMatchingNotFound": {
"message": "No matching results found"
"message": "No matching results found."
},
"shorthandVersion": {
"message": "v$1",
@ -3539,6 +3542,9 @@
"testFaucet": {
"message": "Test Faucet"
},
"testNetworks": {
"message": "Test networks"
},
"theme": {
"message": "Theme"
},

View File

@ -130,6 +130,13 @@ export const CHAIN_ID_TO_RPC_URL_MAP = {
[LOCALHOST_CHAIN_ID]: LOCALHOST_RPC_URL,
};
export const CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP = {
[MAINNET_CHAIN_ID]: ETH_TOKEN_IMAGE_URL,
[AVALANCHE_CHAIN_ID]: AVAX_TOKEN_IMAGE_URL,
[BSC_CHAIN_ID]: BNB_TOKEN_IMAGE_URL,
[POLYGON_CHAIN_ID]: MATIC_TOKEN_IMAGE_URL,
};
export const CHAIN_ID_TO_NETWORK_ID_MAP = Object.values(
NETWORK_TYPE_TO_ID_MAP,
).reduce((chainIdToNetworkIdMap, { chainId, networkId }) => {

View File

@ -0,0 +1,114 @@
import React, { useState, useContext } from 'react';
import PropTypes from 'prop-types';
import Fuse from 'fuse.js';
import InputAdornment from '@material-ui/core/InputAdornment';
import TextField from '../../../../components/ui/text-field';
import { I18nContext } from '../../../../contexts/i18n';
import SearchIcon from '../../../../components/ui/search-icon';
export default function CustomContentSearch({
onSearch,
error,
networksList,
searchQueryInput,
}) {
const t = useContext(I18nContext);
const [searchIconColor, setSearchIconColor] = useState(
'var(--color-icon-muted)',
);
const networksListArray = Object.values(networksList);
const networksSearchFuse = new Fuse(networksListArray, {
shouldSort: true,
threshold: 0.2,
location: 0,
distance: 100,
maxPatternLength: 32,
minMatchCharLength: 1,
keys: ['label', 'labelKey'],
});
const handleSearch = async (searchQuery) => {
if (searchQuery === '') {
setSearchIconColor('var(--color-icon-muted)');
} else {
setSearchIconColor('var(--color-icon-default)');
}
const fuseSearchResult = networksSearchFuse.search(searchQuery);
const results = searchQuery ? [...fuseSearchResult] : networksListArray;
await onSearch({ searchQuery, results });
};
const renderStartAdornment = () => {
return (
<InputAdornment position="start">
<SearchIcon color={searchIconColor} />
</InputAdornment>
);
};
const renderEndAdornment = () => {
return (
<>
{searchQueryInput && (
<InputAdornment
className="imageclosectn"
position="end"
onClick={() => handleSearch('')}
>
<i
className="fa fa-times networks-tab__imageclose"
width="17"
heigth="17"
title="Close"
/>
</InputAdornment>
)}
</>
);
};
return (
<TextField
id="search-networks"
data-testid="search-networks"
placeholder={t('customContentSearch')}
type="text"
value={searchQueryInput}
onChange={(e) => handleSearch(e.target.value)}
error={error}
fullWidth
autoFocus
autoComplete="off"
classes={{
inputRoot: 'networks-tab__networks-list__custom-search-network',
}}
startAdornment={renderStartAdornment()}
endAdornment={renderEndAdornment()}
/>
);
}
CustomContentSearch.propTypes = {
/**
* The function searches the list of networks depending on
* the entered parameter and returns the entire list of
* networks when the user clicks on 'X' on the search tab
*/
onSearch: PropTypes.func,
/**
* An error message is displayed when a user searches for a specific
* network on the search tab and that network does not exist
* in the networks list
*/
error: PropTypes.string,
/**
* The list of networks available for search.
*/
networksList: PropTypes.array,
/**
* Search for a specific network(s) by label or labelKey
*/
searchQueryInput: PropTypes.string,
};

View File

@ -0,0 +1,23 @@
import React from 'react';
import testData from '../../../../../.storybook/test-data';
import CustomContentSearch from './custom-content-search';
export default {
title: 'Pages/Settings/NetworksTab/CustomContentSearch',
id: __filename,
argTypes: {
error: {
control: 'text',
},
searchQueryInput: {
control: 'text',
},
onSearch: {
action: 'onSearch',
},
},
};
export const CustomContentSearchComponent = (args) => {
return <CustomContentSearch {...args} networksList={testData.networkList} />;
};

View File

@ -0,0 +1,110 @@
import React from 'react';
import { fireEvent, render, screen } from '@testing-library/react';
import Fuse from 'fuse.js';
import configureStore from '../../../../store/store';
import { renderWithProvider } from '../../../../../test/lib/render-helpers';
import testData from '../../../../../.storybook/test-data';
import CustomContentSearch from './custom-content-search';
function renderComponent({ componentProps = {} } = {}) {
const store = configureStore({});
return renderWithProvider(<CustomContentSearch {...componentProps} />, store);
}
describe('CustomContentSearch', () => {
it('should render custom content search correctly', () => {
const onSearch = jest.fn();
const wrapper = renderComponent({
componentProps: { onSearch, networksList: testData.networkList },
});
expect(wrapper.getByTestId('search-networks')).toBeDefined();
});
it('should check placeholder text in TextField input', () => {
const onSearch = jest.fn();
const wrapper = renderComponent({
componentProps: { onSearch, networksList: testData.networkList },
});
const { getByPlaceholderText } = wrapper;
expect(
getByPlaceholderText('Search for a previously added network'),
).toBeInTheDocument();
});
it('re-render the same component with different props', () => {
const onSearch = jest.fn();
const { rerender } = render(
<CustomContentSearch
onSearch={onSearch}
networksList={[]}
searchQueryInput=""
/>,
);
const input = screen.getByTestId('search-networks');
expect(input.value).toBe('');
rerender(
<CustomContentSearch
onSearch={onSearch}
networksList={[]}
searchQueryInput="Polygon"
/>,
);
expect(input.value).toBe('Polygon');
});
it('should call onSearch prop with input value', () => {
const onSearch = jest.fn();
const wrapper = renderComponent({
componentProps: {
onSearch,
networksList: [],
searchQueryInput: 'Avalanche',
},
});
const input = wrapper.getByTestId('search-networks');
fireEvent.change(input, { target: { value: 'Polygon' } });
expect(input.value).toBe('Avalanche');
});
it('should check if error is shown if search does not return any network from the list', () => {
const onSearch = jest.fn();
const networksSearchFuse = new Fuse(testData.networkList, {
keys: ['label', 'labelKey'],
});
const fuseSearchResult = networksSearchFuse.search('Optimism');
const wrapper = renderComponent({
componentProps: {
onSearch,
networksList: testData.networkList,
searchQueryInput: 'Optimism',
error: 'No matching results found.',
},
});
const input = wrapper.getByTestId('search-networks');
expect(fuseSearchResult).toHaveLength(0);
fireEvent.change(input, {
target: { error: 'No matching results found.' },
});
expect(input.error).toBe('No matching results found.');
});
it('should check if error is not shown if search return some network from the list', () => {
const onSearch = jest.fn();
const networksSearchFuse = new Fuse(testData.networkList, {
keys: ['label', 'labelKey'],
});
const fuseSearchResult = networksSearchFuse.search('ropsten');
const wrapper = renderComponent({
componentProps: {
onSearch,
networksList: testData.networkList,
searchQueryInput: 'Avalanche',
error: '',
},
});
const input = wrapper.getByTestId('search-networks');
expect(fuseSearchResult).toHaveLength(1);
fireEvent.change(input, { target: { error: '' } });
expect(input.error).toBe('');
});
});

View File

@ -0,0 +1 @@
export { default } from './custom-content-search';

View File

@ -1,10 +1,46 @@
@use "design-system";
.networks-tab {
&__imageclose {
cursor: pointer;
color: var(--color-icon-default);
}
&__content {
display: flex;
height: 100%;
max-width: 739px;
max-width: 779px;
justify-content: space-between;
&__custom-image {
border: 1px solid var(--color-border-default);
margin-inline-start: 8px;
}
&__check-icon {
margin-inline-end: 10px;
color: var(--color-success-default);
&__transparent {
color: transparent;
width: 16px;
margin-inline-end: 10px;
}
}
&__icon-with-fallback {
padding: 0 1px 2px 2px;
color: var(--color-primary-inverse); // TODO: design-tokens needs network colors
margin-inline-start: 8px;
@each $variant, $color in design-system.$color-map {
&--color-#{$variant} {
background: var($color);
color: var(--color-primary-inverse); // TODO: design-tokens needs network colors
}
}
}
@media screen and (max-width: $break-small) {
margin-top: 0;
flex-direction: column;
@ -52,7 +88,9 @@
flex-direction: column;
justify-content: space-between;
max-height: 465px;
max-width: 400px;
margin-top: 24px;
padding-inline-start: 24px;
.page-container__footer {
border-top: none;
@ -74,6 +112,7 @@
align-items: center;
width: 90%;
margin-top: 10px;
padding: 0;
}
}
@ -103,8 +142,38 @@
&__networks-list {
flex: 0.5 0 auto;
max-width: 343px;
margin-top: 24px;
max-width: 350px;
border-right: 1px solid var(--color-border-default);
&__custom-search-network {
margin-top: 24px;
}
.MuiInput-input {
font-size: 14px;
@media screen and (max-width: $break-small) {
font-size: 12px;
}
}
.MuiTextField-root {
padding-inline-end: 16px;
#search-networks-helper-text {
color: var(--color-text-alternative);
}
@media screen and (max-width: $break-small) {
padding: 0 24px 0 24px;
}
}
&__label {
@media screen and (max-width: $break-small) {
margin-inline-start: 58px;
}
}
@media screen and (max-width: $break-small) {
flex: 1;
@ -149,9 +218,10 @@
&__networks-list-item {
display: flex;
padding: 13px 0 13px 17px;
padding: 12px 24px 12px 0;
position: relative;
align-items: center;
width: 311px;
.color-indicator {
&:hover {
@ -159,13 +229,12 @@
}
@media screen and (max-width: $break-small) {
margin: 0 4px 0 10px;
margin: 0 4px 0 20px;
}
}
@media screen and (max-width: $break-small) {
padding: 20px 23px 21px 17px;
border-bottom: 1px solid var(--color-border-default);
padding: 12px 0 12px 24px;
max-width: 351px;
}
}
@ -191,35 +260,31 @@
}
svg {
margin-left: 10px;
margin-inline-start: 15px;
padding-top: 3px;
}
}
&__networks-list-arrow {
display: none;
@media screen and (max-width: $break-small) {
display: block;
right: 10px;
cursor: pointer;
position: absolute;
margin: 0 5px;
[dir='rtl'] & {
transform: rotate(180deg);
}
color: var(--color-text-default);
}
}
&__networks-list-name--selected {
font-weight: bold;
color: var(--color-text-default);
@media screen and (max-width: $break-small) {
font-weight: normal;
color: var(--color-text-default);
}
}
&__networks-list-name--disabled {
font-weight: 300;
color: var(--color-text-muted);
@media screen and (max-width: $break-small) {
color: var(--color-text-default);
}
}
&__network-form-footer {

View File

@ -4,16 +4,19 @@ import classnames from 'classnames';
import { useDispatch, useSelector } from 'react-redux';
import { useHistory } from 'react-router-dom';
import { useI18nContext } from '../../../../hooks/useI18nContext';
import { NETWORK_TYPE_RPC } from '../../../../../shared/constants/network';
import { SIZES } from '../../../../helpers/constants/design-system';
import ColorIndicator from '../../../../components/ui/color-indicator';
import {
NETWORK_TYPE_RPC,
CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP,
} from '../../../../../shared/constants/network';
import LockIcon from '../../../../components/ui/lock-icon';
import IconCaretRight from '../../../../components/ui/icon/icon-caret-right';
import IconCheck from '../../../../components/ui/icon/icon-check';
import { NETWORKS_FORM_ROUTE } from '../../../../helpers/constants/routes';
import { setSelectedSettingsRpcUrl } from '../../../../store/actions';
import { getEnvironmentType } from '../../../../../app/scripts/lib/util';
import { ENVIRONMENT_TYPE_FULLSCREEN } from '../../../../../shared/constants/app';
import { getProvider } from '../../../../selectors';
import Identicon from '../../../../components/ui/identicon';
import UrlIcon from '../../../../components/ui/url-icon';
import { handleHooksSettingsRefs } from '../../../../helpers/utils/settings-search';
@ -22,6 +25,8 @@ const NetworksListItem = ({
networkIsSelected,
selectedRpcUrl,
networkIndex,
setSearchQuery,
setSearchedNetworks,
}) => {
const t = useI18nContext();
const history = useHistory();
@ -45,6 +50,9 @@ const NetworksListItem = ({
(listItemUrlIsProviderUrl || listItemTypeIsProviderNonRpcType);
const displayNetworkListItemAsSelected =
listItemNetworkIsSelected || listItemNetworkIsCurrentProvider;
const isCurrentRpcTarget =
listItemUrlIsProviderUrl || listItemTypeIsProviderNonRpcType;
const settingsRefs = useRef();
useEffect(() => {
@ -57,17 +65,46 @@ const NetworksListItem = ({
key={`settings-network-list-item:${rpcUrl}`}
className="networks-tab__networks-list-item"
onClick={() => {
setSearchQuery('');
setSearchedNetworks([]);
dispatch(setSelectedSettingsRpcUrl(rpcUrl));
if (!isFullScreen) {
history.push(NETWORKS_FORM_ROUTE);
}
}}
>
<ColorIndicator
color={labelKey}
type={ColorIndicator.TYPES.FILLED}
size={SIZES.LG}
/>
{isCurrentRpcTarget ? (
<IconCheck color="var(--color-success-default)" />
) : (
<div className="networks-tab__content__check-icon__transparent"></div>
)}
{network.chainId in CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP ? (
<Identicon
className="networks-tab__content__custom-image"
diameter={24}
image={CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP[network.chainId]}
imageBorder
/>
) : (
!network.isATestNetwork && (
<UrlIcon
className="networks-tab__content__icon-with-fallback"
fallbackClassName="networks-tab__content__icon-with-fallback"
name={label}
/>
)
)}
{network.isATestNetwork && (
<UrlIcon
name={label || labelKey}
fallbackClassName={classnames(
'networks-tab__content__icon-with-fallback',
{
[`networks-tab__content__icon-with-fallback--color-${labelKey}`]: true,
},
)}
/>
)}
<div
className={classnames('networks-tab__networks-list-name', {
'networks-tab__networks-list-name--selected': displayNetworkListItemAsSelected,
@ -81,7 +118,6 @@ const NetworksListItem = ({
<LockIcon width="14px" height="17px" fill="var(--color-icon-muted)" />
)}
</div>
<IconCaretRight className="networks-tab__networks-list-arrow" />
</div>
);
};
@ -91,6 +127,8 @@ NetworksListItem.propTypes = {
networkIsSelected: PropTypes.bool,
selectedRpcUrl: PropTypes.string,
networkIndex: PropTypes.number,
setSearchQuery: PropTypes.func,
setSearchedNetworks: PropTypes.func,
};
export default NetworksListItem;

View File

@ -0,0 +1,28 @@
import React from 'react';
import testData from '../../../../../.storybook/test-data';
import NetworksList from './networks-list';
export default {
title: 'Pages/Settings/NetworksTab/NetworksList',
id: __filename,
argTypes: {
networkDefaultedToProvider: {
control: 'boolean',
},
networkIsSelected: {
control: 'boolean',
},
networksToRender: {
control: 'array',
},
},
args: {
networkDefaultedToProvider: false,
networkIsSelected: false,
networksToRender: testData.networkList,
},
};
export const NetworksListComponent = (args) => {
return <NetworksList {...args} />;
};

View File

@ -1,6 +1,13 @@
import React from 'react';
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import { useI18nContext } from '../../../../hooks/useI18nContext';
import CustomContentSearch from '../custom-content-search';
import Typography from '../../../../components/ui/typography';
import {
COLORS,
TYPOGRAPHY,
} from '../../../../helpers/constants/design-system';
import NetworksListItem from '../networks-list-item';
const NetworksList = ({
@ -9,6 +16,20 @@ const NetworksList = ({
networkDefaultedToProvider,
selectedRpcUrl,
}) => {
const t = useI18nContext();
const [searchedNetworks, setSearchedNetworks] = useState([]);
const [searchQuery, setSearchQuery] = useState('');
const searchedNetworksToRender =
searchedNetworks.length === 0 && searchQuery === ''
? networksToRender
: searchedNetworks;
const searchedNetworksToRenderThatAreNotTestNetworks = searchedNetworksToRender.filter(
(network) => !network.isATestNetwork,
);
const searchedNetworksToRenderThatAreTestNetworks = searchedNetworksToRender.filter(
(network) => network.isATestNetwork,
);
return (
<div
className={classnames('networks-tab__networks-list', {
@ -16,13 +37,52 @@ const NetworksList = ({
networkIsSelected && !networkDefaultedToProvider,
})}
>
{networksToRender.map((network, index) => (
<CustomContentSearch
onSearch={({
searchQuery: newSearchQuery = '',
results: newResults = [],
}) => {
setSearchedNetworks(newResults);
setSearchQuery(newSearchQuery);
}}
error={
searchedNetworksToRender.length === 0
? t('settingsSearchMatchingNotFound')
: null
}
networksList={networksToRender}
searchQueryInput={searchQuery}
/>
{searchedNetworksToRenderThatAreNotTestNetworks.map((network, index) => (
<NetworksListItem
key={`settings-network-list:${network.rpcUrl}`}
network={network}
networkIsSelected={networkIsSelected}
selectedRpcUrl={selectedRpcUrl}
networkIndex={index}
setSearchQuery={setSearchQuery}
setSearchedNetworks={setSearchedNetworks}
/>
))}
{searchQuery === '' && (
<Typography
variant={TYPOGRAPHY.H6}
margin={[6, 0, 0, 9]}
color={COLORS.TEXT_MUTED}
className="networks-tab__networks-list__label"
>
{t('testNetworks')}
</Typography>
)}
{searchedNetworksToRenderThatAreTestNetworks.map((network, index) => (
<NetworksListItem
key={`settings-network-list:${network.rpcUrl}`}
network={network}
networkIsSelected={networkIsSelected}
selectedRpcUrl={selectedRpcUrl}
networkIndex={index}
setSearchQuery={setSearchQuery}
setSearchedNetworks={setSearchedNetworks}
/>
))}
</div>

View File

@ -25,6 +25,7 @@ const renderComponent = (props) => {
const defaultNetworks = defaultNetworksData.map((network) => ({
...network,
viewOnly: true,
isATestNetwork: true,
}));
const props = {
@ -32,6 +33,7 @@ const props = {
networkIsSelected: false,
networksToRender: defaultNetworks,
selectedRpcUrl: 'http://localhost:8545',
isATestNetwork: true,
};
describe('NetworksList Component', () => {

View File

@ -26,6 +26,7 @@ const renderComponent = (props) => {
const defaultNetworks = defaultNetworksData.map((network) => ({
...network,
viewOnly: true,
isATestNetwork: true,
}));
const props = {
@ -40,13 +41,16 @@ const props = {
blockExplorerUrl: '',
viewOnly: false,
rpcPrefs: {},
isATestNetwork: true,
},
shouldRenderNetworkForm: true,
};
describe('NetworksTabContent Component', () => {
it('should render networks tab content correctly', async () => {
const { queryByText, getByDisplayValue } = renderComponent(props);
const { queryByText, getByDisplayValue, getAllByText } = renderComponent(
props,
);
expect(queryByText('Ethereum Mainnet')).toBeInTheDocument();
expect(queryByText('Ropsten Test Network')).toBeInTheDocument();
@ -68,9 +72,7 @@ describe('NetworksTabContent Component', () => {
getByDisplayValue(props.selectedNetwork.chainId),
).toBeInTheDocument();
expect(getByDisplayValue(props.selectedNetwork.ticker)).toBeInTheDocument();
expect(
getByDisplayValue(props.selectedNetwork.blockExplorerUrl),
).toBeInTheDocument();
expect(getAllByText(props.selectedNetwork.blockExplorerUrl)).toBeDefined();
fireEvent.change(getByDisplayValue(props.selectedNetwork.label), {
target: { value: 'LocalHost 8545' },

View File

@ -16,7 +16,10 @@ import {
getNetworksTabSelectedRpcUrl,
getProvider,
} from '../../../selectors';
import { NETWORK_TYPE_RPC } from '../../../../shared/constants/network';
import {
NETWORK_TYPE_RPC,
TEST_CHAINS,
} from '../../../../shared/constants/network';
import { defaultNetworksData } from './networks-tab.constants';
import NetworksTabContent from './networks-tab-content';
import NetworksForm from './networks-form';
@ -25,6 +28,7 @@ import NetworksFormSubheader from './networks-tab-subheader';
const defaultNetworks = defaultNetworksData.map((network) => ({
...network,
viewOnly: true,
isATestNetwork: TEST_CHAINS.includes(network.chainId),
}));
const NetworksTab = ({ addNewNetwork }) => {
@ -50,6 +54,7 @@ const NetworksTab = ({ addNewNetwork }) => {
chainId: rpc.chainId,
ticker: rpc.ticker,
blockExplorerUrl: rpc.rpcPrefs?.blockExplorerUrl || '',
isATestNetwork: TEST_CHAINS.includes(rpc.chainId),
};
});