1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-12-22 17:33:23 +01:00

Merge branch 'develop' of github.com:MetaMask/metamask-extension into minimal

This commit is contained in:
Matthias Kretschmann 2023-04-20 12:52:57 +01:00
commit c63ffe4f24
Signed by: m
GPG Key ID: 606EEEF3C479A91F
105 changed files with 1607 additions and 912 deletions

View File

@ -2,82 +2,82 @@ export const suggestedAssets = [
{
asset: {
address: '0x6b175474e89094c44da98b954eedeac495271d0f',
symbol: 'META',
symbol: 'ETH',
decimals: 18,
image: 'metamark.svg',
unlisted: false
image: './images/eth_logo.png',
unlisted: false,
},
},
{
asset: {
'address': '0xB8c77482e45F1F44dE1745F52C74426C631bDD52',
'symbol': '0X',
'decimals': 18,
'image': '0x.svg',
'unlisted': false
address: '0xB8c77482e45F1F44dE1745F52C74426C631bDD52',
symbol: '0X',
decimals: 18,
image: '0x.svg',
unlisted: false,
},
},
{
asset: {
'address': '0x1f9840a85d5af5bf1d1762f925bdaddc4201f984',
'symbol': 'AST',
'decimals': 18,
'image': 'ast.png',
'unlisted': false
address: '0x1f9840a85d5af5bf1d1762f925bdaddc4201f984',
symbol: 'AST',
decimals: 18,
image: 'ast.png',
unlisted: false,
},
},
{
asset: {
'address': '0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2',
'symbol': 'BAT',
'decimals': 18,
'image': 'BAT_icon.svg',
'unlisted': false
address: '0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2',
symbol: 'BAT',
decimals: 18,
image: 'BAT_icon.svg',
unlisted: false,
},
},
{
asset: {
'address': '0xe83cccfabd4ed148903bf36d4283ee7c8b3494d1',
'symbol': 'CVL',
'decimals': 18,
'image': 'CVL_token.svg',
'unlisted': false
address: '0xe83cccfabd4ed148903bf36d4283ee7c8b3494d1',
symbol: 'CVL',
decimals: 18,
image: 'CVL_token.svg',
unlisted: false,
},
},
{
asset: {
'address': '0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e',
'symbol': 'GLA',
'decimals': 18,
'image': 'gladius.svg',
'unlisted': false
address: '0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e',
symbol: 'GLA',
decimals: 18,
image: 'gladius.svg',
unlisted: false,
},
},
{
asset: {
'address': '0x467Bccd9d29f223BcE8043b84E8C8B282827790F',
'symbol': 'GNO',
'decimals': 18,
'image': 'gnosis.svg',
'unlisted': false
address: '0x467Bccd9d29f223BcE8043b84E8C8B282827790F',
symbol: 'GNO',
decimals: 18,
image: 'gnosis.svg',
unlisted: false,
},
},
{
asset: {
'address': '0xff20817765cb7f73d4bde2e66e067e58d11095c2',
'symbol': 'OMG',
'decimals': 18,
'image': 'omg.jpg',
'unlisted': false
address: '0xff20817765cb7f73d4bde2e66e067e58d11095c2',
symbol: 'OMG',
decimals: 18,
image: 'omg.jpg',
unlisted: false,
},
},
{
asset: {
'address': '0x8e870d67f660d95d5be530380d0ec0bd388289e1',
'symbol': 'WED',
'decimals': 18,
'image': 'wed.png',
'unlisted': false
address: '0x8e870d67f660d95d5be530380d0ec0bd388289e1',
symbol: 'WED',
decimals: 18,
image: 'wed.png',
unlisted: false,
},
},
]
];

View File

@ -172,9 +172,9 @@ const state = {
},
'0x6b175474e89094c44da98b954eedeac495271d0f': {
address: '0x6b175474e89094c44da98b954eedeac495271d0f',
symbol: 'META',
symbol: 'ETH',
decimals: 18,
image: 'metamark.svg',
image: './images/eth_logo.png',
unlisted: false,
},
'0xB8c77482e45F1F44dE1745F52C74426C631bDD52': {

View File

@ -6,6 +6,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## [10.28.3]
### Fixed
- Fix network switching prompted by dapps for users that added the network prior to v10.28.0. ([#18513](https://github.com/MetaMask/metamask-extension/pull/18513))
## [10.28.2]
### Fixed
- Fix network switching prompted by dapps by fixing the `wallet_switchEthereumChain` handler. ([#18483](https://github.com/MetaMask/metamask-extension/pull/18483))
- Fix to ensure all users see the NFT and transaction security notifications ([#18460](https://github.com/MetaMask/metamask-extension/pull/18460))
- Fix issue blocking Hindi, Japanese and Turkish language users from installing from the Chrome store ([#18487](https://github.com/MetaMask/metamask-extension/pull/18487))
## [10.28.1]
### Changed
- Fix release automation ([#18427](https://github.com/MetaMask/metamask-extension/pull/18427))
@ -3643,7 +3653,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Uncategorized
- Added the ability to restore accounts from seed words.
[Unreleased]: https://github.com/MetaMask/metamask-extension/compare/v10.28.1...HEAD
[Unreleased]: https://github.com/MetaMask/metamask-extension/compare/v10.28.3...HEAD
[10.28.3]: https://github.com/MetaMask/metamask-extension/compare/v10.28.2...v10.28.3
[10.28.2]: https://github.com/MetaMask/metamask-extension/compare/v10.28.1...v10.28.2
[10.28.1]: https://github.com/MetaMask/metamask-extension/compare/v10.28.0...v10.28.1
[10.28.0]: https://github.com/MetaMask/metamask-extension/compare/v10.27.0...v10.28.0
[10.27.0]: https://github.com/MetaMask/metamask-extension/compare/v10.26.2...v10.27.0

View File

@ -2537,6 +2537,19 @@
"message": "Swapping on mobile is here!",
"description": "Title for a notification in the 'See What's New' popup. Tells users that they can now use MetaMask Swaps on Mobile."
},
"notifications20ActionText": {
"message": "Learn more",
"description": "The 'call to action' on the button, or link, of the 'Stay secure' notification. Upon clicking, users will be taken to a ledger page to resolve the U2F connection issue."
},
"notifications20Description": {
"message": "If you're on the latest version of Firefox, you might be experiencing an issue related to Firefox dropping U2F support.",
"description": "Description of a notification in the 'See What's New' popup. Describes the U2F support being dropped by firefox and that it affects ledger users."
},
"notifications20Title": {
"message": "Ledger and Firefox Users Experiencing Connection Issues",
"description": "Title for a notification in the 'See What's New' popup. Tells users that latest firefox users using U2F may experience connection issues."
},
"notifications3ActionText": {
"message": "Read more",
"description": "The 'call to action' on the button, or link, of the 'Stay secure' notification. Upon clicking, users will be taken to a page about security on the metamask support website."
@ -4541,6 +4554,22 @@
"transferFrom": {
"message": "Transfer from"
},
"troubleConnectingToLedgerU2FOnFirefox": {
"message": "We're having trouble connecting your Ledger. $1",
"description": "$1 is a link to the wallet connection guide;"
},
"troubleConnectingToLedgerU2FOnFirefox2": {
"message": "Review our hardware wallet connection guide and try again.",
"description": "$1 of the ledger wallet connection guide"
},
"troubleConnectingToLedgerU2FOnFirefoxLedgerSolution": {
"message": "If you're on the latest version of Firefox, you might be experiencing an issue related to Firefox dropping U2F support. Learn how to fix this issue $1.",
"description": "It is a link to the ledger website for the workaround."
},
"troubleConnectingToLedgerU2FOnFirefoxLedgerSolution2": {
"message": "here",
"description": "Second part of the error message; It is a link to the ledger website for the workaround."
},
"troubleConnectingToWallet": {
"message": "We had trouble connecting to your $1, try reviewing $2 and try again.",
"description": "$1 is the wallet device name; $2 is a link to wallet connection guide"

BIN
app/images/eth_logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -1,18 +0,0 @@
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 500 500" style="enable-background:new 0 0 500 500;" xml:space="preserve">
<style type="text/css">
.st0{fill:#343434;}
.st1{fill:#8C8C8C;}
.st2{fill:#3C3C3B;}
.st3{fill:#141414;}
.st4{fill:#393939;}
</style>
<g id="XMLID_1_">
<polygon id="XMLID_2_" class="st0" points="250,67.5 247.5,75.8 247.5,317.2 250,319.6 362,253.4 "/>
<polygon id="XMLID_3_" class="st1" points="250,67.5 137.9,253.4 250,319.6 250,202.5 "/>
<polygon id="XMLID_4_" class="st2" points="250,340.8 248.6,342.5 248.6,428.5 250,432.5 362.1,274.6 "/>
<polygon id="XMLID_5_" class="st1" points="250,432.5 250,340.8 137.9,274.6 "/>
<polygon id="XMLID_6_" class="st3" points="250,319.6 362,253.4 250,202.5 "/>
<polygon id="XMLID_7_" class="st4" points="137.9,253.4 250,319.6 250,202.5 "/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 919 B

View File

@ -6740,7 +6740,7 @@ describe('NetworkController', () => {
it('throws if the given chain ID is not a 0x-prefixed hex number', async () => {
const invalidChainId = '1';
await withController(async ({ controller }) => {
expect(() =>
await expect(() =>
controller.upsertNetworkConfiguration(
{
/* @ts-expect-error We are intentionally passing bad input. */
@ -6755,7 +6755,7 @@ describe('NetworkController', () => {
source: MetaMetricsNetworkEventSource.Dapp,
},
),
).toThrow(
).rejects.toThrow(
new Error(
`Invalid chain ID "${invalidChainId}": invalid hex string.`,
),
@ -6765,7 +6765,7 @@ describe('NetworkController', () => {
it('throws if the given chain ID is greater than the maximum allowed ID', async () => {
await withController(async ({ controller }) => {
expect(() =>
await expect(() =>
controller.upsertNetworkConfiguration(
{
chainId: '0xFFFFFFFFFFFFFFFF',
@ -6779,7 +6779,7 @@ describe('NetworkController', () => {
source: MetaMetricsNetworkEventSource.Dapp,
},
),
).toThrow(
).rejects.toThrow(
new Error(
'Invalid chain ID "0xFFFFFFFFFFFFFFFF": numerical value greater than max safe value.',
),
@ -6789,7 +6789,7 @@ describe('NetworkController', () => {
it('throws if the no (or a falsy) rpcUrl is passed', async () => {
await withController(async ({ controller }) => {
expect(() =>
await expect(() =>
controller.upsertNetworkConfiguration(
/* @ts-expect-error We are intentionally passing bad input. */
{
@ -6803,7 +6803,7 @@ describe('NetworkController', () => {
source: MetaMetricsNetworkEventSource.Dapp,
},
),
).toThrow(
).rejects.toThrow(
new Error(
'An rpcUrl is required to add or update network configuration',
),
@ -6813,7 +6813,7 @@ describe('NetworkController', () => {
it('throws if rpcUrl passed is not a valid Url', async () => {
await withController(async ({ controller }) => {
expect(() =>
await expect(() =>
controller.upsertNetworkConfiguration(
{
chainId: '0x9999',
@ -6827,13 +6827,13 @@ describe('NetworkController', () => {
source: MetaMetricsNetworkEventSource.Dapp,
},
),
).toThrow(new Error('rpcUrl must be a valid URL'));
).rejects.toThrow(new Error('rpcUrl must be a valid URL'));
});
});
it('throws if the no (or a falsy) ticker is passed', async () => {
await withController(async ({ controller }) => {
expect(() =>
await expect(() =>
controller.upsertNetworkConfiguration(
/* @ts-expect-error We are intentionally passing bad input. */
{
@ -6847,7 +6847,7 @@ describe('NetworkController', () => {
source: MetaMetricsNetworkEventSource.Dapp,
},
),
).toThrow(
).rejects.toThrow(
new Error(
'A ticker is required to add or update networkConfiguration',
),
@ -6857,7 +6857,7 @@ describe('NetworkController', () => {
it('throws if an options object is not passed as a second argument', async () => {
await withController(async ({ controller }) => {
expect(() =>
await expect(() =>
/* @ts-expect-error We are intentionally passing bad input. */
controller.upsertNetworkConfiguration({
chainId: '0x5',
@ -6865,7 +6865,7 @@ describe('NetworkController', () => {
rpcPrefs: { blockExplorerUrl: 'test-block-explorer.com' },
rpcUrl: 'https://mock-rpc-url',
}),
).toThrow(
).rejects.toThrow(
new Error(
"Cannot read properties of undefined (reading 'setActive')",
),
@ -6888,7 +6888,7 @@ describe('NetworkController', () => {
ticker: 'test_ticker',
};
controller.upsertNetworkConfiguration(rpcUrlNetwork, {
await controller.upsertNetworkConfiguration(rpcUrlNetwork, {
referrer: 'https://test-dapp.com',
source: MetaMetricsNetworkEventSource.Dapp,
});
@ -6926,7 +6926,7 @@ describe('NetworkController', () => {
invalidKey2: {},
};
controller.upsertNetworkConfiguration(rpcUrlNetwork, {
await controller.upsertNetworkConfiguration(rpcUrlNetwork, {
referrer: 'https://test-dapp.com',
source: MetaMetricsNetworkEventSource.Dapp,
});
@ -6975,7 +6975,7 @@ describe('NetworkController', () => {
ticker: 'RPC',
};
controller.upsertNetworkConfiguration(rpcUrlNetwork, {
await controller.upsertNetworkConfiguration(rpcUrlNetwork, {
referrer: 'https://test-dapp.com',
source: MetaMetricsNetworkEventSource.Dapp,
});
@ -7024,7 +7024,7 @@ describe('NetworkController', () => {
rpcPrefs: { blockExplorerUrl: 'alternativetestchainscan.io' },
chainId: '0x1' as const,
};
controller.upsertNetworkConfiguration(updatedConfiguration, {
await controller.upsertNetworkConfiguration(updatedConfiguration, {
referrer: 'https://test-dapp.com',
source: MetaMetricsNetworkEventSource.Dapp,
});
@ -7069,7 +7069,7 @@ describe('NetworkController', () => {
},
},
async ({ controller }) => {
controller.upsertNetworkConfiguration(
await controller.upsertNetworkConfiguration(
{
rpcUrl: 'https://test-rpc-url',
ticker: 'new-ticker',
@ -7136,7 +7136,7 @@ describe('NetworkController', () => {
ticker: 'test_ticker',
};
controller.upsertNetworkConfiguration(rpcUrlNetwork, {
await controller.upsertNetworkConfiguration(rpcUrlNetwork, {
referrer: 'https://test-dapp.com',
source: MetaMetricsNetworkEventSource.Dapp,
});
@ -7180,7 +7180,7 @@ describe('NetworkController', () => {
ticker: 'test_ticker',
};
controller.upsertNetworkConfiguration(rpcUrlNetwork, {
await controller.upsertNetworkConfiguration(rpcUrlNetwork, {
setActive: true,
referrer: 'https://test-dapp.com',
source: MetaMetricsNetworkEventSource.Dapp,
@ -7229,7 +7229,7 @@ describe('NetworkController', () => {
rpcPrefs: { blockExplorerUrl: 'https://block-explorer' },
};
controller.upsertNetworkConfiguration(newNetworkConfiguration, {
await controller.upsertNetworkConfiguration(newNetworkConfiguration, {
referrer: 'https://test-dapp.com',
source: MetaMetricsNetworkEventSource.Dapp,
});
@ -7296,10 +7296,10 @@ describe('NetworkController', () => {
rpcPrefs: { blockExplorerUrl: 'https://block-explorer' },
};
expect(() => {
await expect(() =>
/* @ts-expect-error We are intentionally passing bad input. */
controller.upsertNetworkConfiguration(newNetworkConfiguration, {});
}).toThrow(
controller.upsertNetworkConfiguration(newNetworkConfiguration, {}),
).rejects.toThrow(
'referrer and source are required arguments for adding or updating a network configuration',
);
},
@ -7308,14 +7308,14 @@ describe('NetworkController', () => {
});
describe('removeNetworkConfigurations', () => {
it('should remove a network configuration', async () => {
const networkConfigurationId = 'networkConfigurationId';
it('removes a network configuration', async () => {
const networkConfigurationId = 'testNetworkConfigurationId';
await withController(
{
state: {
networkConfigurations: {
[networkConfigurationId]: {
id: 'aaaaaa',
id: networkConfigurationId,
rpcUrl: 'https://test-rpc-url',
ticker: 'old_rpc_ticker',
nickname: 'old_rpc_chainName',
@ -7326,25 +7326,44 @@ describe('NetworkController', () => {
},
},
async ({ controller }) => {
expect(
Object.values(controller.store.getState().networkConfigurations),
).toStrictEqual([
{
id: 'aaaaaa',
rpcUrl: 'https://test-rpc-url',
ticker: 'old_rpc_ticker',
nickname: 'old_rpc_chainName',
rpcPrefs: { blockExplorerUrl: 'testchainscan.io' },
chainId: '0x1',
},
]);
controller.removeNetworkConfiguration(networkConfigurationId);
expect(
controller.store.getState().networkConfigurations,
).toStrictEqual({});
},
);
});
it('throws if the networkConfigurationId it is passed does not correspond to a network configuration in state', async () => {
const testNetworkConfigurationId = 'testNetworkConfigurationId';
const invalidNetworkConfigurationId = 'invalidNetworkConfigurationId';
await withController(
{
state: {
networkConfigurations: {
[testNetworkConfigurationId]: {
rpcUrl: 'https://rpc-url.com',
ticker: 'old_rpc_ticker',
nickname: 'old_rpc_nickname',
rpcPrefs: { blockExplorerUrl: 'testchainscan.io' },
chainId: '0x1',
id: testNetworkConfigurationId,
},
},
},
},
async ({ controller }) => {
expect(() =>
controller.removeNetworkConfiguration(
invalidNetworkConfigurationId,
),
).toThrow(
`networkConfigurationId ${invalidNetworkConfigurationId} does not match a configured networkConfiguration`,
);
},
);
});
});
});

View File

@ -1034,7 +1034,7 @@ export class NetworkController extends EventEmitter {
* @throws if `rpcUrl` is not a valid URL.
* @returns The ID for the added or updated network configuration.
*/
upsertNetworkConfiguration(
async upsertNetworkConfiguration(
{
rpcUrl,
chainId,
@ -1051,7 +1051,7 @@ export class NetworkController extends EventEmitter {
referrer: string;
source: string;
},
): NetworkConfigurationId {
): Promise<NetworkConfigurationId> {
assert.ok(
isPrefixedFormattedHexString(chainId),
`Invalid chain ID "${chainId}": invalid hex string.`,
@ -1129,7 +1129,7 @@ export class NetworkController extends EventEmitter {
}
if (setActive) {
this.setActiveNetwork(newNetworkConfigurationId);
await this.setActiveNetwork(newNetworkConfigurationId);
}
return newNetworkConfigurationId;
@ -1141,9 +1141,12 @@ export class NetworkController extends EventEmitter {
* @param networkConfigurationId - The unique id for the network configuration
* to remove.
*/
removeNetworkConfiguration(
networkConfigurationId: NetworkConfigurationId,
): void {
removeNetworkConfiguration(networkConfigurationId: NetworkConfigurationId) {
if (!this.store.getState().networkConfigurations[networkConfigurationId]) {
throw new Error(
`networkConfigurationId ${networkConfigurationId} does not match a configured networkConfiguration`,
);
}
const networkConfigurations = {
...this.store.getState().networkConfigurations,
};

View File

@ -113,7 +113,7 @@ export default class SwapsController {
fetchTradesInfo = defaultFetchTradesInfo,
getCurrentChainId,
getEIP1559GasFeeEstimates,
onNetworkDidChange,
onNetworkStateChange,
}) {
this.store = new ObservableStore({
swapsState: { ...initialState.swapsState },
@ -137,7 +137,7 @@ export default class SwapsController {
this.ethersProvider = new Web3Provider(provider);
this._currentNetworkId = networkController.store.getState().networkId;
onNetworkDidChange(() => {
onNetworkStateChange(() => {
const { networkId, networkStatus } = networkController.store.getState();
if (
networkStatus === NetworkStatus.Available &&

View File

@ -160,7 +160,7 @@ describe('SwapsController', function () {
return new SwapsController({
getBufferedGasLimit: MOCK_GET_BUFFERED_GAS_LIMIT,
networkController: getMockNetworkController(),
onNetworkDidChange: sinon.stub(),
onNetworkStateChange: sinon.stub(),
provider: _provider,
getProviderConfig: MOCK_GET_PROVIDER_CONFIG,
getTokenRatesState: MOCK_TOKEN_RATES_STORE,
@ -208,11 +208,11 @@ describe('SwapsController', function () {
it('should replace ethers instance when network changes', function () {
const networkController = getMockNetworkController();
const onNetworkDidChange = sinon.stub();
const onNetworkStateChange = sinon.stub();
const swapsController = new SwapsController({
getBufferedGasLimit: MOCK_GET_BUFFERED_GAS_LIMIT,
networkController,
onNetworkDidChange,
onNetworkStateChange,
provider,
getProviderConfig: MOCK_GET_PROVIDER_CONFIG,
getTokenRatesState: MOCK_TOKEN_RATES_STORE,
@ -220,7 +220,7 @@ describe('SwapsController', function () {
getCurrentChainId: getCurrentChainIdStub,
});
const currentEthersInstance = swapsController.ethersProvider;
const changeNetwork = onNetworkDidChange.getCall(0).args[0];
const changeNetwork = onNetworkStateChange.getCall(0).args[0];
networkController.store.getState.returns({
networkId: NETWORK_IDS.MAINNET,
@ -238,11 +238,11 @@ describe('SwapsController', function () {
it('should not replace ethers instance when network changes to loading', function () {
const networkController = getMockNetworkController();
const onNetworkDidChange = sinon.stub();
const onNetworkStateChange = sinon.stub();
const swapsController = new SwapsController({
getBufferedGasLimit: MOCK_GET_BUFFERED_GAS_LIMIT,
networkController,
onNetworkDidChange,
onNetworkStateChange,
provider,
getProviderConfig: MOCK_GET_PROVIDER_CONFIG,
getTokenRatesState: MOCK_TOKEN_RATES_STORE,
@ -250,7 +250,7 @@ describe('SwapsController', function () {
getCurrentChainId: getCurrentChainIdStub,
});
const currentEthersInstance = swapsController.ethersProvider;
const changeNetwork = onNetworkDidChange.getCall(0).args[0];
const changeNetwork = onNetworkStateChange.getCall(0).args[0];
networkController.store.getState.returns({
networkId: null,
@ -268,11 +268,11 @@ describe('SwapsController', function () {
it('should not replace ethers instance when network changes to the same network', function () {
const networkController = getMockNetworkController();
const onNetworkDidChange = sinon.stub();
const onNetworkStateChange = sinon.stub();
const swapsController = new SwapsController({
getBufferedGasLimit: MOCK_GET_BUFFERED_GAS_LIMIT,
networkController,
onNetworkDidChange,
onNetworkStateChange,
provider,
getProviderConfig: MOCK_GET_PROVIDER_CONFIG,
getTokenRatesState: MOCK_TOKEN_RATES_STORE,
@ -280,7 +280,7 @@ describe('SwapsController', function () {
getCurrentChainId: getCurrentChainIdStub,
});
const currentEthersInstance = swapsController.ethersProvider;
const changeNetwork = onNetworkDidChange.getCall(0).args[0];
const changeNetwork = onNetworkStateChange.getCall(0).args[0];
networkController.store.getState.returns({
networkId: NETWORK_IDS.GOERLI,

View File

@ -1195,10 +1195,8 @@ export default class MetamaskController extends EventEmitter {
this.txController.txGasUtil,
),
networkController: this.networkController,
onNetworkDidChange: networkControllerMessenger.subscribe.bind(
networkControllerMessenger,
NetworkControllerEventType.NetworkDidChange,
),
onNetworkStateChange: (listener) =>
this.networkController.store.subscribe(listener),
provider: this.provider,
getProviderConfig: () => this.networkController.store.getState().provider,
getTokenRatesState: () => this.tokenRatesController.state,

View File

@ -1,103 +1,254 @@
import { migrate } from './083';
import { v4 } from 'uuid';
import { migrate, version } from './083';
describe('migration #83', () => {
it('updates the version metadata', async () => {
const originalVersionedData = buildOriginalVersionedData({
meta: {
version: 9999999,
},
});
jest.mock('uuid', () => {
const actual = jest.requireActual('uuid');
const newVersionedData = await migrate(originalVersionedData);
expect(newVersionedData.meta).toStrictEqual({
version: 83,
});
});
it('does not change the state if the network controller state does not exist', async () => {
const originalVersionedData = buildOriginalVersionedData({
data: {
test: '123',
},
});
const newVersionedData = await migrate(originalVersionedData);
expect(newVersionedData.data).toStrictEqual(originalVersionedData.data);
});
const nonObjects = [undefined, null, 'test', 1, ['test']];
for (const invalidState of nonObjects) {
it(`does not change the state if the network controller state is ${invalidState}`, async () => {
const originalVersionedData = buildOriginalVersionedData({
data: {
NetworkController: invalidState,
},
});
const newVersionedData = await migrate(originalVersionedData);
expect(newVersionedData.data).toStrictEqual(originalVersionedData.data);
});
}
it('does not change the state if the network controller state does not include "network"', async () => {
const originalVersionedData = buildOriginalVersionedData({
data: {
NetworkController: {
test: '123',
},
},
});
const newVersionedData = await migrate(originalVersionedData);
expect(newVersionedData.data).toStrictEqual(originalVersionedData.data);
});
it('replaces "network" in the network controller state with "networkId": null, "networkStatus": "unknown" if it is "loading"', async () => {
const originalVersionedData = buildOriginalVersionedData({
data: {
NetworkController: {
network: 'loading',
},
},
});
const newVersionedData = await migrate(originalVersionedData);
expect(newVersionedData.data).toStrictEqual({
NetworkController: {
networkId: null,
networkStatus: 'unknown',
},
});
});
it('replaces "network" in the network controller state with "networkId": network, "networkStatus": "available" if it is not "loading"', async () => {
const originalVersionedData = buildOriginalVersionedData({
data: {
NetworkController: {
network: '12345',
},
},
});
const newVersionedData = await migrate(originalVersionedData);
expect(newVersionedData.data).toStrictEqual({
NetworkController: {
networkId: '12345',
networkStatus: 'available',
},
});
});
return {
...actual,
v4: jest.fn(),
};
});
function buildOriginalVersionedData({ meta = {}, data = {} } = {}) {
return {
meta: { version: 999999, ...meta },
data: { ...data },
};
}
describe('migration #83', () => {
beforeEach(() => {
v4.mockImplementationOnce(() => 'network-configuration-id-1')
.mockImplementationOnce(() => 'network-configuration-id-2')
.mockImplementationOnce(() => 'network-configuration-id-3')
.mockImplementationOnce(() => 'network-configuration-id-4');
});
afterEach(() => {
jest.resetAllMocks();
});
it('should update the version metadata', async () => {
const oldStorage = {
meta: {
version: 82,
},
data: {},
};
const newStorage = await migrate(oldStorage);
expect(newStorage.meta).toStrictEqual({
version,
});
});
it('should use the key of the networkConfigurations object to set the id of each network configuration', async () => {
const oldStorage = {
meta: {
version,
},
data: {
NetworkController: {
networkConfigurations: {
'network-configuration-id-1': {
chainId: '0x539',
nickname: 'Localhost 8545',
rpcPrefs: {},
rpcUrl: 'http://localhost:8545',
ticker: 'ETH',
},
'network-configuration-id-2': {
chainId: '0xa4b1',
nickname: 'Arbitrum One',
rpcPrefs: {
blockExplorerUrl: 'https://explorer.arbitrum.io',
},
rpcUrl:
'https://arbitrum-mainnet.infura.io/v3/373266a93aab4acda48f89d4fe77c748',
ticker: 'ETH',
},
'network-configuration-id-3': {
chainId: '0x4e454152',
nickname: 'Aurora Mainnet',
rpcPrefs: {
blockExplorerUrl: 'https://aurorascan.dev/',
},
rpcUrl:
'https://aurora-mainnet.infura.io/v3/373266a93aab4acda48f89d4fe77c748',
ticker: 'Aurora ETH',
},
'network-configuration-id-4': {
chainId: '0x38',
nickname:
'BNB Smart Chain (previously Binance Smart Chain Mainnet)',
rpcPrefs: {
blockExplorerUrl: 'https://bscscan.com/',
},
rpcUrl: 'https://bsc-dataseed.binance.org/',
ticker: 'BNB',
},
},
},
},
};
const newStorage = await migrate(oldStorage);
const expectedNewStorage = {
meta: {
version,
},
data: {
NetworkController: {
networkConfigurations: {
'network-configuration-id-1': {
chainId: '0x539',
nickname: 'Localhost 8545',
rpcPrefs: {},
rpcUrl: 'http://localhost:8545',
ticker: 'ETH',
id: 'network-configuration-id-1',
},
'network-configuration-id-2': {
chainId: '0xa4b1',
nickname: 'Arbitrum One',
rpcPrefs: {
blockExplorerUrl: 'https://explorer.arbitrum.io',
},
rpcUrl:
'https://arbitrum-mainnet.infura.io/v3/373266a93aab4acda48f89d4fe77c748',
ticker: 'ETH',
id: 'network-configuration-id-2',
},
'network-configuration-id-3': {
chainId: '0x4e454152',
nickname: 'Aurora Mainnet',
rpcPrefs: {
blockExplorerUrl: 'https://aurorascan.dev/',
},
rpcUrl:
'https://aurora-mainnet.infura.io/v3/373266a93aab4acda48f89d4fe77c748',
ticker: 'Aurora ETH',
id: 'network-configuration-id-3',
},
'network-configuration-id-4': {
chainId: '0x38',
nickname:
'BNB Smart Chain (previously Binance Smart Chain Mainnet)',
rpcPrefs: {
blockExplorerUrl: 'https://bscscan.com/',
},
rpcUrl: 'https://bsc-dataseed.binance.org/',
ticker: 'BNB',
id: 'network-configuration-id-4',
},
},
},
},
};
expect(newStorage).toStrictEqual(expectedNewStorage);
});
it('should not modify state if state.NetworkController is undefined', async () => {
const oldStorage = {
meta: {
version,
},
data: {
testProperty: 'testValue',
},
};
const newStorage = await migrate(oldStorage);
const expectedNewStorage = {
meta: {
version,
},
data: {
testProperty: 'testValue',
},
};
expect(newStorage).toStrictEqual(expectedNewStorage);
});
it('should not modify state if state.NetworkController is not an object', async () => {
const oldStorage = {
meta: {
version,
},
data: {
NetworkController: false,
testProperty: 'testValue',
},
};
const newStorage = await migrate(oldStorage);
const expectedNewStorage = {
meta: {
version,
},
data: {
NetworkController: false,
testProperty: 'testValue',
},
};
expect(newStorage).toStrictEqual(expectedNewStorage);
});
it('should not modify state if state.NetworkController.networkConfigurations is undefined', async () => {
const oldStorage = {
meta: {
version,
},
data: {
NetworkController: {
testNetworkControllerProperty: 'testNetworkControllerValue',
networkConfigurations: undefined,
},
testProperty: 'testValue',
},
};
const newStorage = await migrate(oldStorage);
const expectedNewStorage = {
meta: {
version,
},
data: {
NetworkController: {
testNetworkControllerProperty: 'testNetworkControllerValue',
networkConfigurations: undefined,
},
testProperty: 'testValue',
},
};
expect(newStorage).toStrictEqual(expectedNewStorage);
});
it('should not modify state if state.NetworkController.networkConfigurations is an empty object', async () => {
const oldStorage = {
meta: {
version,
},
data: {
NetworkController: {
testNetworkControllerProperty: 'testNetworkControllerValue',
networkConfigurations: {},
},
testProperty: 'testValue',
},
};
const newStorage = await migrate(oldStorage);
const expectedNewStorage = {
meta: {
version,
},
data: {
NetworkController: {
testNetworkControllerProperty: 'testNetworkControllerValue',
networkConfigurations: {},
},
testProperty: 'testValue',
},
};
expect(newStorage).toStrictEqual(expectedNewStorage);
});
});

View File

@ -1,10 +1,11 @@
import { cloneDeep } from 'lodash';
import { hasProperty, isObject } from '@metamask/utils';
import { isObject } from '@metamask/utils';
export const version = 83;
/**
* The `network` property in state was replaced with `networkId` and `networkStatus`.
* Ensure that each networkConfigurations object in state.NetworkController.networkConfigurations has an
* `id` property which matches the key pointing that object
*
* @param originalVersionedData - Versioned MetaMask extension state, exactly what we persist to dist.
* @param originalVersionedData.meta - State metadata.
@ -23,25 +24,35 @@ export async function migrate(originalVersionedData: {
}
function transformState(state: Record<string, unknown>) {
if (
!hasProperty(state, 'NetworkController') ||
!isObject(state.NetworkController) ||
!hasProperty(state.NetworkController, 'network')
) {
if (!isObject(state.NetworkController)) {
return state;
}
const { NetworkController } = state;
if (!isObject(NetworkController.networkConfigurations)) {
return state;
}
const NetworkController = { ...state.NetworkController };
const { networkConfigurations } = NetworkController;
if (NetworkController.network === 'loading') {
NetworkController.networkId = null;
NetworkController.networkStatus = 'unknown';
} else {
NetworkController.networkId = NetworkController.network;
NetworkController.networkStatus = 'available';
const newNetworkConfigurations: Record<string, Record<string, unknown>> = {};
for (const networkConfigurationId of Object.keys(networkConfigurations)) {
const networkConfiguration = networkConfigurations[networkConfigurationId];
if (!isObject(networkConfiguration)) {
return state;
}
newNetworkConfigurations[networkConfigurationId] = {
...networkConfiguration,
id: networkConfigurationId,
};
}
delete NetworkController.network;
return { ...state, NetworkController };
return {
...state,
NetworkController: {
...NetworkController,
networkConfigurations: newNetworkConfigurations,
},
};
}

View File

@ -1,254 +1,103 @@
import { v4 } from 'uuid';
import { migrate, version } from './084';
jest.mock('uuid', () => {
const actual = jest.requireActual('uuid');
return {
...actual,
v4: jest.fn(),
};
});
import { migrate } from './084';
describe('migration #84', () => {
beforeEach(() => {
v4.mockImplementationOnce(() => 'network-configuration-id-1')
.mockImplementationOnce(() => 'network-configuration-id-2')
.mockImplementationOnce(() => 'network-configuration-id-3')
.mockImplementationOnce(() => 'network-configuration-id-4');
});
afterEach(() => {
jest.resetAllMocks();
});
it('should update the version metadata', async () => {
const oldStorage = {
it('updates the version metadata', async () => {
const originalVersionedData = buildOriginalVersionedData({
meta: {
version: 83,
version: 9999999,
},
data: {},
};
});
const newStorage = await migrate(oldStorage);
expect(newStorage.meta).toStrictEqual({
version,
const newVersionedData = await migrate(originalVersionedData);
expect(newVersionedData.meta).toStrictEqual({
version: 84,
});
});
it('should use the key of the networkConfigurations object to set the id of each network configuration', async () => {
const oldStorage = {
meta: {
version,
},
it('does not change the state if the network controller state does not exist', async () => {
const originalVersionedData = buildOriginalVersionedData({
data: {
NetworkController: {
networkConfigurations: {
'network-configuration-id-1': {
chainId: '0x539',
nickname: 'Localhost 8545',
rpcPrefs: {},
rpcUrl: 'http://localhost:8545',
ticker: 'ETH',
},
'network-configuration-id-2': {
chainId: '0xa4b1',
nickname: 'Arbitrum One',
rpcPrefs: {
blockExplorerUrl: 'https://explorer.arbitrum.io',
},
rpcUrl:
'https://arbitrum-mainnet.infura.io/v3/373266a93aab4acda48f89d4fe77c748',
ticker: 'ETH',
},
'network-configuration-id-3': {
chainId: '0x4e454152',
nickname: 'Aurora Mainnet',
rpcPrefs: {
blockExplorerUrl: 'https://aurorascan.dev/',
},
rpcUrl:
'https://aurora-mainnet.infura.io/v3/373266a93aab4acda48f89d4fe77c748',
ticker: 'Aurora ETH',
},
'network-configuration-id-4': {
chainId: '0x38',
nickname:
'BNB Smart Chain (previously Binance Smart Chain Mainnet)',
rpcPrefs: {
blockExplorerUrl: 'https://bscscan.com/',
},
rpcUrl: 'https://bsc-dataseed.binance.org/',
ticker: 'BNB',
},
},
},
test: '123',
},
};
});
const newStorage = await migrate(oldStorage);
const newVersionedData = await migrate(originalVersionedData);
const expectedNewStorage = {
meta: {
version,
},
data: {
NetworkController: {
networkConfigurations: {
'network-configuration-id-1': {
chainId: '0x539',
nickname: 'Localhost 8545',
rpcPrefs: {},
rpcUrl: 'http://localhost:8545',
ticker: 'ETH',
id: 'network-configuration-id-1',
},
'network-configuration-id-2': {
chainId: '0xa4b1',
nickname: 'Arbitrum One',
rpcPrefs: {
blockExplorerUrl: 'https://explorer.arbitrum.io',
},
rpcUrl:
'https://arbitrum-mainnet.infura.io/v3/373266a93aab4acda48f89d4fe77c748',
ticker: 'ETH',
id: 'network-configuration-id-2',
},
'network-configuration-id-3': {
chainId: '0x4e454152',
nickname: 'Aurora Mainnet',
rpcPrefs: {
blockExplorerUrl: 'https://aurorascan.dev/',
},
rpcUrl:
'https://aurora-mainnet.infura.io/v3/373266a93aab4acda48f89d4fe77c748',
ticker: 'Aurora ETH',
id: 'network-configuration-id-3',
},
'network-configuration-id-4': {
chainId: '0x38',
nickname:
'BNB Smart Chain (previously Binance Smart Chain Mainnet)',
rpcPrefs: {
blockExplorerUrl: 'https://bscscan.com/',
},
rpcUrl: 'https://bsc-dataseed.binance.org/',
ticker: 'BNB',
id: 'network-configuration-id-4',
},
},
},
},
};
expect(newStorage).toStrictEqual(expectedNewStorage);
expect(newVersionedData.data).toStrictEqual(originalVersionedData.data);
});
it('should not modify state if state.NetworkController is undefined', async () => {
const oldStorage = {
meta: {
version,
},
data: {
testProperty: 'testValue',
},
};
const nonObjects = [undefined, null, 'test', 1, ['test']];
for (const invalidState of nonObjects) {
it(`does not change the state if the network controller state is ${invalidState}`, async () => {
const originalVersionedData = buildOriginalVersionedData({
data: {
NetworkController: invalidState,
},
});
const newStorage = await migrate(oldStorage);
const newVersionedData = await migrate(originalVersionedData);
const expectedNewStorage = {
meta: {
version,
},
expect(newVersionedData.data).toStrictEqual(originalVersionedData.data);
});
}
it('does not change the state if the network controller state does not include "network"', async () => {
const originalVersionedData = buildOriginalVersionedData({
data: {
testProperty: 'testValue',
NetworkController: {
test: '123',
},
},
};
expect(newStorage).toStrictEqual(expectedNewStorage);
});
const newVersionedData = await migrate(originalVersionedData);
expect(newVersionedData.data).toStrictEqual(originalVersionedData.data);
});
it('should not modify state if state.NetworkController is not an object', async () => {
const oldStorage = {
meta: {
version,
},
it('replaces "network" in the network controller state with "networkId": null, "networkStatus": "unknown" if it is "loading"', async () => {
const originalVersionedData = buildOriginalVersionedData({
data: {
NetworkController: false,
testProperty: 'testValue',
NetworkController: {
network: 'loading',
},
},
};
});
const newStorage = await migrate(oldStorage);
const newVersionedData = await migrate(originalVersionedData);
const expectedNewStorage = {
meta: {
version,
expect(newVersionedData.data).toStrictEqual({
NetworkController: {
networkId: null,
networkStatus: 'unknown',
},
data: {
NetworkController: false,
testProperty: 'testValue',
},
};
expect(newStorage).toStrictEqual(expectedNewStorage);
});
});
it('should not modify state if state.NetworkController.networkConfigurations is undefined', async () => {
const oldStorage = {
meta: {
version,
},
it('replaces "network" in the network controller state with "networkId": network, "networkStatus": "available" if it is not "loading"', async () => {
const originalVersionedData = buildOriginalVersionedData({
data: {
NetworkController: {
testNetworkControllerProperty: 'testNetworkControllerValue',
networkConfigurations: undefined,
network: '12345',
},
testProperty: 'testValue',
},
};
});
const newStorage = await migrate(oldStorage);
const newVersionedData = await migrate(originalVersionedData);
const expectedNewStorage = {
meta: {
version,
expect(newVersionedData.data).toStrictEqual({
NetworkController: {
networkId: '12345',
networkStatus: 'available',
},
data: {
NetworkController: {
testNetworkControllerProperty: 'testNetworkControllerValue',
networkConfigurations: undefined,
},
testProperty: 'testValue',
},
};
expect(newStorage).toStrictEqual(expectedNewStorage);
});
it('should not modify state if state.NetworkController.networkConfigurations is an empty object', async () => {
const oldStorage = {
meta: {
version,
},
data: {
NetworkController: {
testNetworkControllerProperty: 'testNetworkControllerValue',
networkConfigurations: {},
},
testProperty: 'testValue',
},
};
const newStorage = await migrate(oldStorage);
const expectedNewStorage = {
meta: {
version,
},
data: {
NetworkController: {
testNetworkControllerProperty: 'testNetworkControllerValue',
networkConfigurations: {},
},
testProperty: 'testValue',
},
};
expect(newStorage).toStrictEqual(expectedNewStorage);
});
});
});
function buildOriginalVersionedData({ meta = {}, data = {} } = {}) {
return {
meta: { version: 999999, ...meta },
data: { ...data },
};
}

View File

@ -1,11 +1,10 @@
import { cloneDeep } from 'lodash';
import { isObject } from '@metamask/utils';
import { hasProperty, isObject } from '@metamask/utils';
export const version = 84;
/**
* Ensure that each networkConfigurations object in state.NetworkController.networkConfigurations has an
* `id` property which matches the key pointing that object
* The `network` property in state was replaced with `networkId` and `networkStatus`.
*
* @param originalVersionedData - Versioned MetaMask extension state, exactly what we persist to dist.
* @param originalVersionedData.meta - State metadata.
@ -24,35 +23,25 @@ export async function migrate(originalVersionedData: {
}
function transformState(state: Record<string, unknown>) {
if (!isObject(state.NetworkController)) {
return state;
}
const { NetworkController } = state;
if (!isObject(NetworkController.networkConfigurations)) {
if (
!hasProperty(state, 'NetworkController') ||
!isObject(state.NetworkController) ||
!hasProperty(state.NetworkController, 'network')
) {
return state;
}
const { networkConfigurations } = NetworkController;
const NetworkController = { ...state.NetworkController };
const newNetworkConfigurations: Record<string, Record<string, unknown>> = {};
for (const networkConfigurationId of Object.keys(networkConfigurations)) {
const networkConfiguration = networkConfigurations[networkConfigurationId];
if (!isObject(networkConfiguration)) {
return state;
}
newNetworkConfigurations[networkConfigurationId] = {
...networkConfiguration,
id: networkConfigurationId,
};
if (NetworkController.network === 'loading') {
NetworkController.networkId = null;
NetworkController.networkStatus = 'unknown';
} else {
NetworkController.networkId = NetworkController.network;
NetworkController.networkStatus = 'available';
}
return {
...state,
NetworkController: {
...NetworkController,
networkConfigurations: newNetworkConfigurations,
},
};
delete NetworkController.network;
return { ...state, NetworkController };
}

View File

@ -6,10 +6,10 @@
// subset of files to check against these targets.
module.exports = {
global: {
lines: 67.8,
branches: 55.84,
statements: 67.13,
functions: 59.66,
lines: 68.5,
branches: 56.34,
statements: 67.86,
functions: 60.69,
},
transforms: {
branches: 100,

View File

@ -72,6 +72,7 @@ const scuttlingConfigBase = {
Date: '',
JSON: '',
encodeURIComponent: '',
console: '',
crypto: '',
// {clear/set}Timeout are "this sensitive"
clearTimeout: 'window',

View File

@ -1,6 +1,6 @@
{
"name": "metamask-crx",
"version": "10.28.1",
"version": "10.28.3",
"private": true,
"repository": {
"type": "git",

View File

@ -221,7 +221,7 @@ export const CURRENCY_SYMBOLS = {
OPTIMISM: 'OP',
} as const;
export const ETH_TOKEN_IMAGE_URL = './images/eth_logo.svg';
export const ETH_TOKEN_IMAGE_URL = './images/eth_logo.png';
export const TEST_ETH_TOKEN_IMAGE_URL = './images/black-eth-logo.svg';
export const BNB_TOKEN_IMAGE_URL = './images/bnb.png';
export const MATIC_TOKEN_IMAGE_URL = './images/matic-token.png';

View File

@ -102,6 +102,10 @@ export const UI_NOTIFICATIONS = {
width: '100%',
},
},
20: {
id: 20,
date: null,
},
};
export const getTranslatedUINotifications = (t, locale) => {
@ -279,5 +283,16 @@ export const getTranslatedUINotifications = (t, locale) => {
)
: '',
},
20: {
...UI_NOTIFICATIONS[20],
title: t('notifications20Title'),
description: [t('notifications20Description')],
actionText: t('notifications20ActionText'),
date: UI_NOTIFICATIONS[20].date
? new Intl.DateTimeFormat(formattedLocale).format(
new Date(UI_NOTIFICATIONS[20].date),
)
: '',
},
};
};

View File

@ -105,7 +105,7 @@ async function withFixtures(options, testSuite) {
});
}
}
await setupMocking(mockServer, testSpecificMock);
const mockedEndpoint = await setupMocking(mockServer, testSpecificMock);
await mockServer.start(8000);
if (
process.env.SELENIUM_BROWSER === 'chrome' &&
@ -143,10 +143,10 @@ async function withFixtures(options, testSuite) {
await testSuite({
driver: driverProxy ?? driver,
mockServer,
contractRegistry,
ganacheServer,
secondaryGanacheServer,
mockedEndpoint,
});
} catch (error) {
failed = true;

View File

@ -32,6 +32,11 @@ async function setupMocking(server, testSpecificMock) {
return {};
},
});
const mockedEndpoint = await testSpecificMock(server);
// Mocks below this line can be overridden by test-specific mocks
await server
.forPost(
'https://arbitrum-mainnet.infura.io/v3/00000000000000000000000000000000',
@ -369,10 +374,6 @@ async function setupMocking(server, testSpecificMock) {
};
});
testSpecificMock(server);
// Mocks below this line can be overridden by test-specific mocks
await server.forGet(STALELIST_URL).thenCallback(() => {
return {
statusCode: 200,
@ -399,6 +400,8 @@ async function setupMocking(server, testSpecificMock) {
},
};
});
return mockedEndpoint;
}
module.exports = { setupMocking };

View File

@ -9,8 +9,6 @@ describe('ENS', function () {
'https://mainnet.infura.io/v3/00000000000000000000000000000000';
async function mockInfura(mockServer) {
await mockServer.reset();
await mockServer.forAnyRequest().thenPassThrough();
await mockServer
.forPost(infuraUrl)
.withJsonBodyIncluding({ method: 'eth_blockNumber' })
@ -103,7 +101,7 @@ describe('ENS', function () {
await driver.clickElement('[data-testid="eth-overview-send"]');
await driver.fill(
await driver.pasteIntoField(
'input[placeholder="Search, public address (0x), or ENS"]',
sampleEnsDomain,
);

View File

@ -3,9 +3,7 @@ const { convertToHexValue, withFixtures } = require('../helpers');
const FixtureBuilder = require('../fixture-builder');
describe('Sentry errors', function () {
async function mockSegment(mockServer) {
mockServer.reset();
await mockServer.forAnyRequest().thenPassThrough();
async function mockSentry(mockServer) {
return await mockServer
.forPost('https://sentry.io/api/0000000/store/')
.thenCallback(() => {
@ -36,9 +34,9 @@ describe('Sentry errors', function () {
ganacheOptions,
title: this.test.title,
failOnConsoleError: false,
testSpecificMock: mockSentry,
},
async ({ driver, mockServer }) => {
const mockedEndpoint = await mockSegment(mockServer);
async ({ driver, mockedEndpoint }) => {
await driver.navigate();
await driver.fill('#password', 'correct horse battery staple');
await driver.press('#password', driver.Key.ENTER);

View File

@ -4,8 +4,6 @@ const FixtureBuilder = require('../fixture-builder');
describe('Segment metrics', function () {
async function mockSegment(mockServer) {
mockServer.reset();
await mockServer.forAnyRequest().thenPassThrough();
return await mockServer
.forPost('https://api.segment.io/v1/batch')
.withJsonBodyIncluding({ batch: [{ type: 'page' }] })
@ -36,18 +34,17 @@ describe('Segment metrics', function () {
.build(),
ganacheOptions,
title: this.test.title,
failOnConsoleError: false,
testSpecificMock: mockSegment,
},
async ({ driver, mockServer }) => {
const mockedEndpoints = await mockSegment(mockServer);
async ({ driver, mockedEndpoint }) => {
await driver.navigate();
await driver.fill('#password', 'correct horse battery staple');
await driver.press('#password', driver.Key.ENTER);
await driver.wait(async () => {
const isPending = await mockedEndpoints.isPending();
const isPending = await mockedEndpoint.isPending();
return isPending === false;
}, 10000);
const mockedRequests = await mockedEndpoints.getSeenRequests();
const mockedRequests = await mockedEndpoint.getSeenRequests();
assert.equal(mockedRequests.length, 3);
const [firstMock, secondMock, thirdMock] = mockedRequests;
let [mockJson] = firstMock.body.json.batch;

View File

@ -8,6 +8,7 @@ describe('Gas API fallback', function () {
.forGet(
'https://gas-api.metaswap.codefi.network/networks/1/suggestedGasFees',
)
.always()
.thenCallback(() => {
return {
statusCode: 200,

View File

@ -1,6 +1,6 @@
const { strict: assert } = require('assert');
const { SMART_CONTRACTS } = require('../seeder/smart-contracts');
const { convertToHexValue, withFixtures, tinyDelayMs } = require('../helpers');
const { convertToHexValue, withFixtures } = require('../helpers');
const FixtureBuilder = require('../fixture-builder');
describe('Send ETH from inside MetaMask using default gas', function () {
@ -334,7 +334,15 @@ describe('Send ETH from dapp using advanced gas controls', function () {
await priorityFeeInput.fill('1');
await driver.clickElement({ text: 'Save', tag: 'button' });
await driver.delay(tinyDelayMs);
await driver.waitForSelector({
css: '.transaction-detail-item:nth-of-type(1) h6:nth-of-type(2)',
text: '0.02367237 ETH',
});
await driver.waitForSelector({
css: '.transaction-detail-item:nth-of-type(2) h6:nth-of-type(2)',
text: '0.02367237 ETH',
});
await driver.clickElement({ text: 'Confirm', tag: 'button' });
await driver.waitUntilXWindowHandles(2);
await driver.switchToWindow(extension);

View File

@ -37,11 +37,7 @@ import Button from '../../ui/button';
import SearchIcon from '../../ui/icon/search-icon';
import { SUPPORT_LINK } from '../../../../shared/lib/ui-utils';
import { IconColor } from '../../../helpers/constants/design-system';
import {
Icon,
ICON_NAMES,
ICON_SIZES,
} from '../../component-library/icon/deprecated';
import { Icon, IconName, IconSize } from '../../component-library';
import KeyRingLabel from './keyring-label';
export function AccountMenuItem(props) {
@ -229,8 +225,8 @@ export default class AccountMenu extends Component {
{isSelected ? (
<Icon
color={IconColor.successDefault}
name={ICON_NAMES.CHECK}
size={ICON_SIZES.LG}
name={IconName.Check}
size={IconSize.Lg}
/>
) : null}
</div>
@ -374,9 +370,7 @@ export default class AccountMenu extends Component {
});
history.push(NEW_ACCOUNT_ROUTE);
}}
icon={
<Icon name={ICON_NAMES.ADD} color={IconColor.iconAlternative} />
}
icon={<Icon name={IconName.Add} color={IconColor.iconAlternative} />}
text={t('createAccount')}
/>
<AccountMenuItem
@ -393,7 +387,7 @@ export default class AccountMenu extends Component {
history.push(IMPORT_ACCOUNT_ROUTE);
}}
icon={
<Icon name={ICON_NAMES.IMPORT} color={IconColor.iconAlternative} />
<Icon name={IconName.Import} color={IconColor.iconAlternative} />
}
text={t('importAccount')}
/>
@ -415,10 +409,7 @@ export default class AccountMenu extends Component {
}
}}
icon={
<Icon
name={ICON_NAMES.HARDWARE}
color={IconColor.iconAlternative}
/>
<Icon name={IconName.Hardware} color={IconColor.iconAlternative} />
}
text={t('connectHardwareWallet')}
/>
@ -433,7 +424,7 @@ export default class AccountMenu extends Component {
}}
icon={
<div className="account-menu__notifications">
<Icon name={ICON_NAMES.NOTIFICATION} size={ICON_SIZES.LG} />
<Icon name={IconName.Notification} size={IconSize.Lg} />
{unreadNotificationsCount > 0 && (
<div className="account-menu__notifications__count">
{unreadNotificationsCount}
@ -466,10 +457,7 @@ export default class AccountMenu extends Component {
global.platform.openTab({ url: supportLink });
}}
icon={
<Icon
name={ICON_NAMES.MESSAGES}
color={IconColor.iconAlternative}
/>
<Icon name={IconName.Messages} color={IconColor.iconAlternative} />
}
text={supportText}
/>
@ -488,7 +476,7 @@ export default class AccountMenu extends Component {
}}
icon={
<Icon
name={ICON_NAMES.SETTING}
name={IconName.Setting}
color={IconColor.iconAlternative}
ariaLabel={t('settings')}
/>

View File

@ -36,12 +36,7 @@ import { FEATURED_RPCS } from '../../../../shared/constants/network';
import { ADD_NETWORK_ROUTE } from '../../../helpers/constants/routes';
import { getEnvironmentType } from '../../../../app/scripts/lib/util';
import ZENDESK_URLS from '../../../helpers/constants/zendesk-url';
import { Text } from '../../component-library';
import {
Icon,
ICON_NAMES,
ICON_SIZES,
} from '../../component-library/icon/deprecated';
import { Text, Icon, IconName, IconSize } from '../../component-library';
import { MetaMetricsNetworkEventSource } from '../../../../shared/constants/metametrics';
const AddNetwork = () => {
@ -249,9 +244,9 @@ const AddNetwork = () => {
>
<Icon
className="add-network__warning-icon"
name={ICON_NAMES.DANGER}
name={IconName.Danger}
color={IconColor.iconMuted}
size={ICON_SIZES.SM}
size={IconSize.Sm}
/>
</Tooltip>
)

View File

@ -1,5 +1,5 @@
import React from 'react';
import { Icon, ICON_NAMES } from '../../component-library/icon/deprecated';
import { Icon, IconName } from '../../component-library';
import ApproveContentCard from './approve-content-card';
export default {
@ -72,7 +72,7 @@ export default {
},
args: {
showHeader: true,
symbol: <Icon name={ICON_NAMES.TAG} />,
symbol: <Icon name={IconName.Tag} />,
title: 'Transaction fee',
showEdit: true,
showAdvanceGasFeeOptions: true,

View File

@ -16,11 +16,7 @@ import { INVALID_ASSET_TYPE } from '../../../helpers/constants/error-keys';
import { MetaMetricsEventCategory } from '../../../../shared/constants/metametrics';
import { AssetType } from '../../../../shared/constants/transaction';
import { MetaMetricsContext } from '../../../contexts/metametrics';
import {
Icon,
ICON_NAMES,
ICON_SIZES,
} from '../../component-library/icon/deprecated';
import { Icon, IconName, IconSize } from '../../component-library';
import Box from '../../ui/box/box';
const AssetListItem = ({
@ -35,7 +31,6 @@ const AssetListItem = ({
warning,
primary,
secondary,
identiconBorder,
isERC721,
}) => {
const t = useI18nContext();
@ -137,7 +132,6 @@ const AssetListItem = ({
address={tokenAddress}
image={tokenImage}
alt={`${primary} ${tokenSymbol}`}
imageBorder={identiconBorder}
/>
}
midContent={midContent}
@ -145,9 +139,9 @@ const AssetListItem = ({
!isERC721 && (
<Box>
<Icon
name={ICON_NAMES.ARROW_RIGHT}
name={IconName.ArrowRight}
color={Color.iconDefault}
size={ICON_SIZES.SM}
size={IconSize.Sm}
style={{ verticalAlign: 'middle' }}
/>
{sendTokenButton}
@ -170,7 +164,6 @@ AssetListItem.propTypes = {
warning: PropTypes.node,
primary: PropTypes.string,
secondary: PropTypes.string,
identiconBorder: PropTypes.bool,
isERC721: PropTypes.bool,
};

View File

@ -34,9 +34,6 @@ export default {
secondary: {
control: 'text',
},
identiconBorder: {
control: 'boolean',
},
isERC721: {
control: 'boolean',
},
@ -44,7 +41,7 @@ export default {
args: {
tokenAddress: '0x2170ed0880ac9a755fd29b2688956bd959f933f8',
tokenSymbol: 'ETH',
tokenImage: './images/eth_logo.svg',
tokenImage: './images/eth_logo.png',
identiconBorder: true,
},
};

View File

@ -14,11 +14,7 @@ import {
import { BETA_BUGS_URL } from '../../../helpers/constants/beta';
import { hideBetaHeader } from '../../../store/actions';
import { ButtonIcon } from '../../component-library/button-icon/deprecated';
import {
ICON_NAMES,
ICON_SIZES,
} from '../../component-library/icon/deprecated';
import { ButtonIcon, ButtonIconSize, IconName } from '../../component-library';
const BetaHeader = () => {
const t = useI18nContext();
@ -51,8 +47,8 @@ const BetaHeader = () => {
])}
</Typography>
<ButtonIcon
iconName={ICON_NAMES.CLOSE}
size={ICON_SIZES.SM}
iconName={IconName.Close}
size={ButtonIconSize.Sm}
color={IconColor.warningInverse}
className="beta-header__button"
data-testid="beta-header-close"

View File

@ -10,7 +10,7 @@ import Identicon from '../../../ui/identicon';
import { shortenAddress } from '../../../../helpers/utils/util';
import AccountMismatchWarning from '../../../ui/account-mismatch-warning/account-mismatch-warning.component';
import { useI18nContext } from '../../../../hooks/useI18nContext';
import { Icon, ICON_NAMES } from '../../../component-library/icon/deprecated';
import { Icon, IconName } from '../../../component-library';
export default function ConfirmPageContainerHeader({
onEdit,
@ -54,7 +54,7 @@ export default function ConfirmPageContainerHeader({
visibility: showEdit ? 'initial' : 'hidden',
}}
>
<Icon name={ICON_NAMES.ARROW_LEFT} />
<Icon name={IconName.ArrowLeft} />
<span
data-testid="confirm-page-back-edit-button"
className="confirm-page-container-header__back-button"

View File

@ -14,12 +14,7 @@ import {
AlignItems,
IconColor,
} from '../../../helpers/constants/design-system';
import {
Icon,
ICON_NAMES,
ICON_SIZES,
} from '../../component-library/icon/deprecated';
import { Text } from '../../component-library';
import { Icon, IconName, IconSize, Text } from '../../component-library';
const ConfirmationWarningModal = ({ onSubmit, onCancel }) => {
const t = useI18nContext();
@ -60,10 +55,10 @@ const ConfirmationWarningModal = ({ onSubmit, onCancel }) => {
className="confirmation-warning-modal__content__header"
>
<Icon
name={ICON_NAMES.DANGER}
name={IconName.Danger}
color={IconColor.errorDefault}
className="confirmation-warning-modal__content__header__warning-icon"
size={ICON_SIZES.XL}
size={IconSize.Xl}
/>
<Text
variant={TextVariant.headingSm}

View File

@ -1,8 +1,7 @@
import PropTypes from 'prop-types';
import React, { useRef } from 'react';
import { Menu } from '../../../ui/menu';
import { ICON_NAMES } from '../../../component-library/icon/deprecated';
import { ButtonIcon } from '../../../component-library/button-icon/deprecated';
import { IconName, ButtonIcon } from '../../../component-library';
import { useI18nContext } from '../../../../hooks/useI18nContext';
const ConnectedAccountsListOptions = ({
@ -17,7 +16,7 @@ const ConnectedAccountsListOptions = ({
return (
<div ref={ref}>
<ButtonIcon
iconName={ICON_NAMES.MORE_VERTICAL}
iconName={IconName.MoreVertical}
className="connected-accounts-options__button"
onClick={onShowOptions}
ariaLabel={t('options')}

View File

@ -1,6 +1,6 @@
import PropTypes from 'prop-types';
import React, { PureComponent } from 'react';
import { ICON_NAMES } from '../../component-library/icon/deprecated';
import { IconName } from '../../component-library';
import { MenuItem } from '../../ui/menu';
import ConnectedAccountsListItem from './connected-accounts-list-item';
import ConnectedAccountsListOptions from './connected-accounts-list-options';
@ -105,7 +105,7 @@ export default class ConnectedAccountsList extends PureComponent {
onShowOptions={this.showAccountOptions.bind(null, address)}
show={accountWithOptionsShown === address}
>
<MenuItem iconName={ICON_NAMES.LOGOUT} onClick={this.disconnectAccount}>
<MenuItem iconName={IconName.Logout} onClick={this.disconnectAccount}>
{t('disconnectThisAccount')}
</MenuItem>
</ConnectedAccountsListOptions>

View File

@ -42,7 +42,7 @@ exports[`CustomSpendingCap should match snapshot 1`] = `
tabindex="0"
>
<span
class="box mm-icon mm-icon--size-md box--display-inline-block box--flex-direction-row box--color-inherit"
class="box mm-icon mm-icon--size-inherit box--display-inline-block box--flex-direction-row box--color-inherit"
style="mask-image: url('./images/icons/question.svg');"
/>
</div>

View File

@ -8,11 +8,7 @@ import {
DISPLAY,
TypographyVariant,
} from '../../../helpers/constants/design-system';
import {
Icon,
ICON_NAMES,
ICON_SIZES,
} from '../../component-library/icon/deprecated';
import { Icon, IconName, IconSize } from '../../component-library';
export const CustomSpendingCapTooltip = ({
tooltipContentText,
@ -35,13 +31,15 @@ export const CustomSpendingCapTooltip = ({
>
{tooltipIcon ? (
<Icon
name={ICON_NAMES.DANGER}
name={IconName.Danger}
className="form-field__heading-title__tooltip__warning-icon"
size={ICON_SIZES.AUTO}
size={IconSize.Auto}
style={{ 'vertical-align': 'bottom' }}
/>
) : (
tooltipIcon !== '' && <Icon name={ICON_NAMES.QUESTION} />
tooltipIcon !== '' && (
<Icon name={IconName.Question} size={IconSize.Inherit} />
)
)}
</Tooltip>
</Box>

View File

@ -8,8 +8,7 @@ import { addHexPrefix } from 'ethereumjs-util';
import { I18nContext } from '../../../contexts/i18n';
import Box from '../../ui/box';
import FormField from '../../ui/form-field';
import { Text, ButtonLink } from '../../component-library';
import { Icon, ICON_NAMES } from '../../component-library/icon/deprecated';
import { Text, ButtonLink, Icon, IconName } from '../../component-library';
import {
AlignItems,
DISPLAY,
@ -196,7 +195,7 @@ export default function CustomSpendingCap({
as="h6"
color={TextColor.errorDefault}
>
<Icon name={ICON_NAMES.WARNING} /> {t('beCareful')}
<Icon name={IconName.Warning} /> {t('beCareful')}
</Text>,
])
: t('inputLogicEmptyState');

View File

@ -32,7 +32,7 @@ import {
ADD_POPULAR_CUSTOM_NETWORK,
ADVANCED_ROUTE,
} from '../../../helpers/constants/routes';
import { Icon, ICON_NAMES } from '../../component-library/icon/deprecated';
import { Icon, IconName } from '../../component-library';
import { Dropdown, DropdownMenuItem } from './dropdown';
@ -186,7 +186,7 @@ class NetworkDropdown extends Component {
}}
>
{isCurrentRpcTarget ? (
<Icon name={ICON_NAMES.CHECK} color={IconColor.successDefault} />
<Icon name={IconName.Check} color={IconColor.successDefault} />
) : (
<div className="network-check__transparent"></div>
)}
@ -245,7 +245,7 @@ class NetworkDropdown extends Component {
style={DROP_DOWN_MENU_ITEM_STYLE}
>
{providerType === network ? (
<Icon name={ICON_NAMES.CHECK} color={IconColor.successDefault} />
<Icon name={IconName.Check} color={IconColor.successDefault} />
) : (
<div className="network-check__transparent"></div>
)}
@ -314,7 +314,7 @@ class NetworkDropdown extends Component {
style={DROP_DOWN_MENU_ITEM_STYLE}
>
{isCurrentRpcTarget ? (
<Icon name={ICON_NAMES.CHECK} color={IconColor.successDefault} />
<Icon name={IconName.Check} color={IconColor.successDefault} />
) : (
<div className="network-check__transparent"></div>
)}

View File

@ -14,11 +14,7 @@ import { useTransactionEventFragment } from '../../../hooks/useTransactionEventF
import { useTransactionModalContext } from '../../../contexts/transaction-modal';
import InfoTooltip from '../../ui/info-tooltip/info-tooltip';
import Typography from '../../ui/typography/typography';
import {
Icon,
ICON_NAMES,
ICON_SIZES,
} from '../../component-library/icon/deprecated';
import { Icon, IconName, IconSize } from '../../component-library';
export default function EditGasFeeButton({ userAcknowledgedGasMissing }) {
const t = useI18nContext();
@ -78,9 +74,9 @@ export default function EditGasFeeButton({ userAcknowledgedGasMissing }) {
)}
<span className="edit-gas-fee-button__label">{t(title)}</span>
<Icon
name={ICON_NAMES.ARROW_RIGHT}
name={IconName.ArrowRight}
color={Color.primaryDefault}
size={ICON_SIZES.XS}
size={IconSize.Xs}
/>
</button>
{estimateUsed === 'custom' && (

View File

@ -14,11 +14,7 @@ import {
Color,
} from '../../../../helpers/constants/design-system';
import { useCopyToClipboard } from '../../../../hooks/useCopyToClipboard';
import {
Icon,
ICON_NAMES,
ICON_SIZES,
} from '../../../component-library/icon/deprecated';
import { Icon, IconName, IconSize } from '../../../component-library';
export const Copyable = ({ text }) => {
const [copied, handleCopy] = useCopyToClipboard();
@ -51,14 +47,14 @@ export const Copyable = ({ text }) => {
>
{copied ? (
<Icon
name={ICON_NAMES.COPY_SUCCESS}
size={ICON_SIZES.LG}
name={IconName.CopySuccess}
size={IconSize.Lg}
color={Color.iconAlternative}
/>
) : (
<Icon
name={ICON_NAMES.COPY}
size={ICON_SIZES.LG}
name={IconName.Copy}
size={IconSize.Lg}
color={Color.iconAlternative}
onClick={() => handleCopy(text)}
/>

View File

@ -20,12 +20,8 @@ import {
getSnapName,
removeSnapIdPrefix,
} from '../../../../helpers/utils/util';
import { ButtonIcon } from '../../../component-library/button-icon/deprecated';
import { Text } from '../../../component-library';
import {
ICON_NAMES,
ICON_SIZES,
} from '../../../component-library/icon/deprecated';
import { ButtonIcon, IconName, Text } from '../../../component-library';
import { getTargetSubjectMetadata } from '../../../../selectors';
import SnapAvatar from '../snap-avatar';
@ -85,9 +81,8 @@ const SnapAuthorship = ({ snapId, className }) => {
rel="noopener noreferrer"
target="_blank"
href={url}
iconName={ICON_NAMES.EXPORT}
iconName={IconName.Export}
color={IconColor.infoDefault}
size={ICON_SIZES.MD}
style={{ marginLeft: 'auto' }}
/>
</Box>

View File

@ -17,11 +17,9 @@ import {
BadgeWrapperPosition,
AvatarIcon,
AvatarBase,
IconName,
IconSize,
} from '../../../component-library';
import {
ICON_NAMES,
ICON_SIZES,
} from '../../../component-library/icon/deprecated';
import { getTargetSubjectMetadata } from '../../../../selectors';
const SnapAvatar = ({ snapId, className }) => {
@ -40,11 +38,11 @@ const SnapAvatar = ({ snapId, className }) => {
className={classnames('snap-avatar', className)}
badge={
<AvatarIcon
iconName={ICON_NAMES.SNAPS}
size={ICON_SIZES.XS}
iconName={IconName.Snaps}
size={IconSize.Xs}
backgroundColor={IconColor.infoDefault}
iconProps={{
size: ICON_SIZES.XS,
size: IconSize.Xs,
color: IconColor.infoInverse,
}}
/>

View File

@ -16,7 +16,7 @@ import {
} from '../../../../helpers/constants/design-system';
import Button from '../../../ui/button';
import Box from '../../../ui/box/box';
import { Icon, ICON_NAMES } from '../../../component-library/icon/deprecated';
import { Icon, IconName } from '../../../component-library';
export default function SnapContentFooter({ snapName, snapId }) {
const t = useI18nContext();
@ -36,7 +36,7 @@ export default function SnapContentFooter({ snapName, snapId }) {
className="snap-content-footer"
>
<Icon
name={ICON_NAMES.WARNING}
name={IconName.Warning}
size={Size.SM}
color={IconColor.iconMuted}
paddingRight={1}

View File

@ -12,11 +12,12 @@ import {
TextColor,
} from '../../../../helpers/constants/design-system';
import Box from '../../../ui/box';
import { AvatarIcon, Text } from '../../../component-library';
import {
ICON_NAMES,
ICON_SIZES,
} from '../../../component-library/icon/deprecated';
AvatarIcon,
Text,
IconName,
IconSize,
} from '../../../component-library';
import {
DelineatorType,
getDelineatorTitle,
@ -45,14 +46,14 @@ export const SnapDelineator = ({
padding={1}
>
<AvatarIcon
iconName={ICON_NAMES.SNAPS}
size={ICON_SIZES.XS}
iconName={IconName.Snaps}
size={IconSize.Xs}
backgroundColor={
isError ? IconColor.errorDefault : IconColor.infoDefault
}
margin={1}
iconProps={{
size: ICON_SIZES.XS,
size: IconSize.Xs,
color: IconColor.infoInverse,
}}
/>

View File

@ -15,8 +15,7 @@ import {
} from '../../../../helpers/constants/design-system';
import Popover from '../../../ui/popover';
import Button from '../../../ui/button';
import { AvatarIcon, Text } from '../../../component-library';
import { ICON_NAMES } from '../../../component-library/icon/deprecated';
import { AvatarIcon, Text, IconName } from '../../../component-library';
import Box from '../../../ui/box/box';
/**
@ -79,7 +78,7 @@ export default function SnapInstallWarning({ onCancel, onSubmit, warnings }) {
>
<Box justifyContent={JustifyContent.center} marginBottom={6}>
<AvatarIcon
iconName={ICON_NAMES.DANGER}
iconName={IconName.Danger}
backgroundColor={BackgroundColor.warningMuted}
color={IconColor.warningDefault}
size={Size.LG}

View File

@ -28,7 +28,7 @@ import {
MetaMetricsEventName,
} from '../../../../shared/constants/metametrics';
import { MetaMetricsContext } from '../../../contexts/metametrics';
import { ICON_NAMES } from '../../component-library/icon/deprecated';
import { IconName } from '../../component-library';
export default function AccountOptionsMenu({ anchorElement, onClose }) {
const t = useI18nContext();
@ -87,7 +87,7 @@ export default function AccountOptionsMenu({ anchorElement, onClose }) {
</span>
) : null
}
iconName={ICON_NAMES.EXPORT}
iconName={IconName.Export}
>
{t(
blockExplorerLinkText.firstPart,
@ -109,7 +109,7 @@ export default function AccountOptionsMenu({ anchorElement, onClose }) {
global.platform.openExtensionInBrowser();
onClose();
}}
iconName={ICON_NAMES.EXPAND}
iconName={IconName.Expand}
>
{t('expandView')}
</MenuItem>
@ -127,7 +127,7 @@ export default function AccountOptionsMenu({ anchorElement, onClose }) {
});
onClose();
}}
iconName={ICON_NAMES.SCAN_BARCODE}
iconName={IconName.ScanBarcode}
>
{t('accountDetails')}
</MenuItem>
@ -144,7 +144,7 @@ export default function AccountOptionsMenu({ anchorElement, onClose }) {
history.push(CONNECTED_ROUTE);
onClose();
}}
iconName={ICON_NAMES.CONNECT}
iconName={IconName.Connect}
>
{t('connectedSites')}
</MenuItem>
@ -160,7 +160,7 @@ export default function AccountOptionsMenu({ anchorElement, onClose }) {
);
onClose();
}}
iconName={ICON_NAMES.TRASH}
iconName={IconName.Trash}
>
{t('removeAccount')}
</MenuItem>

View File

@ -14,8 +14,7 @@ import { CONNECTED_ACCOUNTS_ROUTE } from '../../../helpers/constants/routes';
import { useI18nContext } from '../../../hooks/useI18nContext';
import { getOriginOfCurrentTab } from '../../../selectors';
import { MetaMetricsContext } from '../../../contexts/metametrics';
import { ButtonIcon } from '../../component-library/button-icon/deprecated';
import { ICON_NAMES } from '../../component-library/icon/deprecated';
import { ButtonIcon, IconName } from '../../component-library';
import AccountOptionsMenu from './account-options-menu';
export default function MenuBar() {
@ -41,7 +40,7 @@ export default function MenuBar() {
<SelectedAccount />
<span style={{ display: 'inherit' }} ref={ref}>
<ButtonIcon
iconName={ICON_NAMES.MORE_VERTICAL}
iconName={IconName.MoreVertical}
className="menu-bar__account-options"
data-testid="account-options-menu-button"
ariaLabel={t('accountOptions')}

View File

@ -25,8 +25,7 @@ import { useCopyToClipboard } from '../../../../hooks/useCopyToClipboard';
import { getAddressBookEntry } from '../../../../selectors';
import { TokenStandard } from '../../../../../shared/constants/transaction';
import NftCollectionImage from '../../../ui/nft-collection-image/nft-collection-image';
import { ButtonIcon } from '../../../component-library/button-icon/deprecated';
import { ICON_NAMES } from '../../../component-library/icon/deprecated';
import { ButtonIcon, IconName } from '../../../component-library';
export default function ContractDetailsModal({
onClose,
@ -147,9 +146,7 @@ export default function ContractDetailsModal({
<ButtonIcon
display={DISPLAY.FLEX}
iconName={
copiedTokenAddress
? ICON_NAMES.COPY_SUCCESS
: ICON_NAMES.COPY
copiedTokenAddress ? IconName.CopySuccess : IconName.Copy
}
onClick={() => handleCopyTokenAddress(tokenAddress)}
color={Color.iconMuted}
@ -163,7 +160,7 @@ export default function ContractDetailsModal({
<Tooltip position="top" title={t('openInBlockExplorer')}>
<ButtonIcon
display={DISPLAY.FLEX}
iconName={ICON_NAMES.EXPORT}
iconName={IconName.Export}
color={Color.iconMuted}
onClick={() => {
const blockExplorerTokenLink = getAccountLink(
@ -244,7 +241,7 @@ export default function ContractDetailsModal({
<ButtonIcon
display={DISPLAY.FLEX}
iconName={
copiedToAddress ? ICON_NAMES.COPY_SUCCESS : ICON_NAMES.COPY
copiedToAddress ? IconName.CopySuccess : IconName.Copy
}
onClick={() => handleCopyToAddress(toAddress)}
color={Color.iconMuted}
@ -258,7 +255,7 @@ export default function ContractDetailsModal({
<Tooltip position="top" title={t('openInBlockExplorer')}>
<ButtonIcon
display={DISPLAY.FLEX}
iconName={ICON_NAMES.EXPORT}
iconName={IconName.Export}
color={Color.iconMuted}
onClick={() => {
const blockExplorerTokenLink = getAccountLink(

View File

@ -15,11 +15,11 @@ import Box from '../../../ui/box';
import withModalProps from '../../../../helpers/higher-order-components/with-modal-props';
import { useI18nContext } from '../../../../hooks/useI18nContext';
import ZENDESK_URLS from '../../../../helpers/constants/zendesk-url';
import { ButtonIcon } from '../../../component-library/button-icon/deprecated';
import {
ICON_NAMES,
ICON_SIZES,
} from '../../../component-library/icon/deprecated';
ButtonIcon,
ButtonIconSize,
IconName,
} from '../../../component-library';
const CustomizeNonce = ({
hideModal,
@ -58,9 +58,9 @@ const CustomizeNonce = ({
{t('editNonceField')}
</Typography>
<ButtonIcon
iconName={ICON_NAMES.CLOSE}
iconName={IconName.Close}
className="customize-nonce-modal__close"
size={ICON_SIZES.SM}
size={ButtonIconSize.Sm}
ariaLabel={t('close')}
onClick={hideModal}
/>

View File

@ -10,11 +10,11 @@ import {
calcTokenAmount,
toPrecisionWithoutTrailingZeros,
} from '../../../../../shared/lib/transactions-controller-utils';
import { ButtonIcon } from '../../../component-library/button-icon/deprecated';
import {
ICON_SIZES,
ICON_NAMES,
} from '../../../component-library/icon/deprecated';
ButtonIcon,
ButtonIconSize,
IconName,
} from '../../../component-library';
const MAX_UNSIGNED_256_INT = new BigNumber(2).pow(256).minus(1).toString(10);
@ -63,8 +63,8 @@ export default class EditApprovalPermission extends PureComponent {
{t('editPermission')}
</div>
<ButtonIcon
iconName={ICON_NAMES.CLOSE}
size={ICON_SIZES.LG}
iconName={IconName.Close}
size={ButtonIconSize.Lg}
className="edit-approval-permission__header__close"
onClick={hideModal}
/>

View File

@ -2,9 +2,13 @@ import PropTypes from 'prop-types';
import React from 'react';
import withModalProps from '../../../../helpers/higher-order-components/with-modal-props';
import Box from '../../../ui/box';
import { Text, Button, BUTTON_TYPES } from '../../../component-library';
import { ButtonIcon } from '../../../component-library/button-icon/deprecated';
import { ICON_NAMES } from '../../../component-library/icon/deprecated';
import {
Text,
Button,
BUTTON_TYPES,
ButtonIcon,
IconName,
} from '../../../component-library';
import {
AlignItems,
DISPLAY,
@ -47,7 +51,7 @@ const HoldToRevealModal = ({ onLongPressed, hideModal }) => {
<Text variant={TextVariant.headingSm}>{t('holdToRevealTitle')}</Text>
<ButtonIcon
className="hold-to-reveal-modal__close"
iconName={ICON_NAMES.CLOSE}
iconName={IconName.Close}
size={Size.SM}
onClick={handleCancel}
ariaLabel={t('close')}

View File

@ -1,8 +1,7 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import Button from '../../../ui/button/button.component';
import { ButtonIcon } from '../../../component-library/button-icon/deprecated';
import { ICON_NAMES } from '../../../component-library/icon/deprecated';
import { ButtonIcon, IconName } from '../../../component-library';
export default class NewAccountModal extends Component {
static contextTypes = {
@ -49,7 +48,7 @@ export default class NewAccountModal extends Component {
className="new-account-modal__content__header-close"
ariaLabel={t('close')}
onClick={this.props.hideModal}
iconName={ICON_NAMES.CLOSE}
iconName={IconName.Close}
/>
</div>
<div className="new-account-modal__input-label">

View File

@ -1,11 +1,7 @@
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import Modal from '../../modal';
import {
Icon,
ICON_NAMES,
ICON_SIZES,
} from '../../../component-library/icon/deprecated';
import { Icon, IconName, IconSize } from '../../../component-library';
import { IconColor } from '../../../../helpers/constants/design-system';
export default class TransactionConfirmed extends PureComponent {
@ -35,9 +31,9 @@ export default class TransactionConfirmed extends PureComponent {
<Modal onSubmit={this.handleSubmit} submitText={t('ok')}>
<div className="transaction-confirmed__content">
<Icon
name={ICON_NAMES.CHECK}
name={IconName.Check}
color={IconColor.successDefault}
size={ICON_SIZES.XL}
size={IconSize.Xl}
/>
<div className="transaction-confirmed__title">
{`${t('confirmed')}!`}

View File

@ -18,11 +18,7 @@ import {
import Chip from '../../ui/chip/chip';
import { useI18nContext } from '../../../hooks/useI18nContext';
import { isNetworkLoading } from '../../../selectors';
import {
Icon,
ICON_NAMES,
ICON_SIZES,
} from '../../component-library/icon/deprecated';
import { Icon, IconName, IconSize } from '../../component-library';
export default function NetworkDisplay({
indicatorSize,
@ -70,9 +66,7 @@ export default function NetworkDisplay({
</LoadingIndicator>
}
rightIcon={
onClick ? (
<Icon name={ICON_NAMES.ARROW_DOWN} size={ICON_SIZES.XS} />
) : null
onClick ? <Icon name={IconName.ArrowDown} size={IconSize.Xs} /> : null
}
label={
networkType === NETWORK_TYPES.RPC

View File

@ -53,8 +53,7 @@ import {
TokenStandard,
} from '../../../../shared/constants/transaction';
import NftDefaultImage from '../nft-default-image';
import { ButtonIcon } from '../../component-library/button-icon/deprecated';
import { ICON_NAMES } from '../../component-library/icon/deprecated';
import { ButtonIcon, IconName } from '../../component-library';
import Tooltip from '../../ui/tooltip';
import { decWEIToDecETH } from '../../../../shared/modules/conversion.utils';
@ -427,7 +426,7 @@ export default function NftDetails({ nft }) {
handleAddressCopy(address);
}}
iconName={
addressCopied ? ICON_NAMES.COPY_SUCCESS : ICON_NAMES.COPY
addressCopied ? IconName.CopySuccess : IconName.Copy
}
/>
</Tooltip>

View File

@ -3,8 +3,7 @@ import PropTypes from 'prop-types';
import { I18nContext } from '../../../contexts/i18n';
import { Menu, MenuItem } from '../../ui/menu';
import { ButtonIcon } from '../../component-library/button-icon/deprecated';
import { ICON_NAMES } from '../../component-library/icon/deprecated';
import { ButtonIcon, IconName } from '../../component-library';
import { Color } from '../../../helpers/constants/design-system';
const NftOptions = ({ onRemove, onViewOnOpensea }) => {
@ -15,7 +14,7 @@ const NftOptions = ({ onRemove, onViewOnOpensea }) => {
return (
<div ref={ref}>
<ButtonIcon
iconName={ICON_NAMES.MORE_VERTICAL}
iconName={IconName.MoreVertical}
className="nft-options__button"
data-testid="nft-options__button"
onClick={() => setNftOptionsOpen(true)}
@ -31,7 +30,7 @@ const NftOptions = ({ onRemove, onViewOnOpensea }) => {
>
{onViewOnOpensea ? (
<MenuItem
iconName={ICON_NAMES.EXPORT}
iconName={IconName.Export}
data-testid="nft-options__view-on-opensea"
onClick={() => {
setNftOptionsOpen(false);
@ -42,7 +41,7 @@ const NftOptions = ({ onRemove, onViewOnOpensea }) => {
</MenuItem>
) : null}
<MenuItem
iconName={ICON_NAMES.TRASH}
iconName={IconName.Trash}
data-testid="nft-item-remove"
onClick={() => {
setNftOptionsOpen(false);

View File

@ -30,7 +30,7 @@ import { updateNftDropDownState } from '../../../store/actions';
import { usePrevious } from '../../../hooks/usePrevious';
import { getNftsDropdownState } from '../../../ducks/metamask/metamask';
import { useI18nContext } from '../../../hooks/useI18nContext';
import { Icon, ICON_NAMES } from '../../component-library/icon/deprecated';
import { Icon, IconName } from '../../component-library';
import NftDefaultImage from '../nft-default-image';
const width =
@ -158,9 +158,7 @@ export default function NftsItems({
</Box>
<Box alignItems={AlignItems.flexEnd}>
<Icon
name={
isExpanded ? ICON_NAMES.ARROW_DOWN : ICON_NAMES.ARROW_RIGHT
}
name={isExpanded ? IconName.ArrowDown : IconName.ArrowRight}
color={Color.iconDefault}
/>
</Box>

View File

@ -11,12 +11,13 @@ import {
TextColor,
TextVariant,
} from '../../../helpers/constants/design-system';
import { AvatarIcon, Text } from '../../component-library';
import {
AvatarIcon,
Text,
Icon,
ICON_NAMES,
ICON_SIZES,
} from '../../component-library/icon/deprecated';
IconName,
IconSize,
} from '../../component-library';
import { formatDate } from '../../../helpers/utils/util';
import { useI18nContext } from '../../../hooks/useI18nContext';
import Tooltip from '../../ui/tooltip';
@ -32,24 +33,24 @@ const PermissionCell = ({
const t = useI18nContext();
let infoIconColor = IconColor.iconMuted;
let infoIcon = ICON_NAMES.INFO;
let iconColor = Color.primaryDefault;
let infoIcon = IconName.Info;
let iconColor = IconColor.primaryDefault;
let iconBackgroundColor = Color.primaryMuted;
if (!revoked && weight === 1) {
iconColor = Color.warningDefault;
iconColor = IconColor.warningDefault;
iconBackgroundColor = Color.warningMuted;
infoIconColor = IconColor.warningDefault;
infoIcon = ICON_NAMES.DANGER;
infoIcon = IconName.Danger;
}
if (dateApproved) {
iconColor = Color.iconMuted;
iconColor = IconColor.iconMuted;
iconBackgroundColor = Color.backgroundAlternative;
}
if (revoked) {
iconColor = Color.iconMuted;
iconColor = IconColor.iconMuted;
iconBackgroundColor = Color.backgroundAlternative;
}
@ -72,9 +73,9 @@ const PermissionCell = ({
{typeof permissionIcon === 'string' ? (
<AvatarIcon
iconName={permissionIcon}
size={ICON_SIZES.MD}
size={IconSize.Md}
iconProps={{
size: ICON_SIZES.SM,
size: IconSize.Sm,
}}
color={iconColor}
backgroundColor={iconBackgroundColor}
@ -108,7 +109,7 @@ const PermissionCell = ({
</Box>
<Box>
<Tooltip html={<div>{description}</div>} position="bottom">
<Icon color={infoIconColor} name={infoIcon} size={ICON_SIZES.SM} />
<Icon color={infoIconColor} name={infoIcon} size={IconSize.Sm} />
</Tooltip>
</Box>
</Box>

View File

@ -16,7 +16,7 @@ import {
} from '../../../helpers/constants/design-system';
import Identicon from '../../ui/identicon';
import { shortenAddress } from '../../../helpers/utils/util';
import { Icon, ICON_NAMES } from '../../component-library/icon/deprecated';
import { Icon, IconName } from '../../component-library';
const SetApproveForAllWarning = ({
collectionName,
@ -62,7 +62,7 @@ const SetApproveForAllWarning = ({
className="set-approval-for-all-warning__content__header"
>
<Icon
name={ICON_NAMES.DANGER}
name={IconName.Danger}
className="set-approval-for-all-warning__content__header__warning-icon"
/>
<Typography

View File

@ -17,7 +17,7 @@ import {
} from '../../../../helpers/constants/design-system';
import Identicon from '../../../ui/identicon';
import { shortenAddress } from '../../../../helpers/utils/util';
import { Icon, ICON_NAMES } from '../../../component-library/icon/deprecated';
import { Icon, IconName } from '../../../component-library';
const SignatureRequestOriginalWarning = ({
senderAddress,
@ -36,7 +36,7 @@ const SignatureRequestOriginalWarning = ({
className="signature-request-warning__content__header"
>
<Icon
name={ICON_NAMES.DANGER}
name={IconName.Danger}
color={IconColor.errorDefault}
className="signature-request-warning__content__header__warning-icon"
/>

View File

@ -6,7 +6,7 @@ import {
JustifyContent,
} from '../../../../helpers/constants/design-system';
import Box from '../../../ui/box';
import { Icon, ICON_NAMES } from '../../../component-library/icon/deprecated';
import { Icon, IconName } from '../../../component-library';
const SignatureRequestSIWEIcon = () => {
return (
@ -17,7 +17,7 @@ const SignatureRequestSIWEIcon = () => {
backgroundColor={Color.errorDefault}
justifyContent={JustifyContent.center}
>
<Icon name={ICON_NAMES.DANGER} color={Color.errorInverse} />
<Icon name={IconName.Danger} color={Color.errorInverse} />
</Box>
);
};

View File

@ -2,11 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import {
Icon,
ICON_NAMES,
ICON_SIZES,
} from '../../component-library/icon/deprecated';
import { Icon, IconName, IconSize } from '../../component-library';
const TabBar = (props) => {
const { tabs = [], onSelect, isActive } = props;
@ -26,8 +22,8 @@ const TabBar = (props) => {
<div className="tab-bar__tab__content__title">{content}</div>
</div>
<Icon
name={ICON_NAMES.ARROW_RIGHT}
size={ICON_SIZES.SM}
name={IconName.ArrowRight}
size={IconSize.Sm}
className="tab-bar__tab__caret"
/>
</button>

View File

@ -1,5 +1,5 @@
import React, { useState } from 'react';
import { Icon, ICON_NAMES } from '../../component-library/icon/deprecated';
import { Icon, IconName } from '../../component-library';
import TabBar from '.';
export default {
@ -19,12 +19,12 @@ export default {
args: {
tabs: [
{
icon: <Icon name={ICON_NAMES.SETTING} />,
icon: <Icon name={IconName.Setting} />,
content: 'General',
key: 'general',
},
{
icon: <Icon name={ICON_NAMES.BOOK} />,
icon: <Icon name={IconName.Book} />,
content: 'Contacts',
key: 'contacts',
},
@ -39,7 +39,7 @@ export default {
key: 'securityAndPrivacy',
},
{
icon: <Icon name={ICON_NAMES.NOTIFICATION} />,
icon: <Icon name={IconName.Notification} />,
content: 'Alerts',
key: 'alerts',
},

View File

@ -12,22 +12,18 @@ import {
TRANSACTION_CANCEL_ATTEMPTED_EVENT,
TRANSACTION_CANCEL_SUCCESS_EVENT,
} from '../transaction-activity-log.constants';
import {
Icon,
ICON_NAMES,
ICON_SIZES,
} from '../../../component-library/icon/deprecated';
import { Icon, IconName, IconSize } from '../../../component-library';
import { Color } from '../../../../helpers/constants/design-system';
export const ACTIVITY_ICONS = {
[TRANSACTION_CREATED_EVENT]: ICON_NAMES.ADD,
[TRANSACTION_SUBMITTED_EVENT]: ICON_NAMES.ARROW_UP,
[TRANSACTION_RESUBMITTED_EVENT]: ICON_NAMES.PROGRAMMING_ARROWS,
[TRANSACTION_CONFIRMED_EVENT]: ICON_NAMES.CHECK,
[TRANSACTION_DROPPED_EVENT]: ICON_NAMES.CLOSE,
[TRANSACTION_ERRORED_EVENT]: ICON_NAMES.DANGER,
[TRANSACTION_CANCEL_ATTEMPTED_EVENT]: ICON_NAMES.CLOSE,
[TRANSACTION_CANCEL_SUCCESS_EVENT]: ICON_NAMES.CLOSE,
[TRANSACTION_CREATED_EVENT]: IconName.Add,
[TRANSACTION_SUBMITTED_EVENT]: IconName.ArrowUp,
[TRANSACTION_RESUBMITTED_EVENT]: IconName.ProgrammingArrows,
[TRANSACTION_CONFIRMED_EVENT]: IconName.Check,
[TRANSACTION_DROPPED_EVENT]: IconName.Close,
[TRANSACTION_ERRORED_EVENT]: IconName.Danger,
[TRANSACTION_CANCEL_ATTEMPTED_EVENT]: IconName.Close,
[TRANSACTION_CANCEL_SUCCESS_EVENT]: IconName.Close,
};
export default class TransactionActivityLogIcon extends PureComponent {
@ -47,7 +43,7 @@ export default class TransactionActivityLogIcon extends PureComponent {
return (
<div className={classnames('transaction-activity-log-icon', className)}>
{icon ? (
<Icon name={icon} color={Color.iconDefault} size={ICON_SIZES.SM} />
<Icon name={icon} color={Color.iconDefault} size={IconSize.Sm} />
) : null}
</div>
);

View File

@ -4,10 +4,7 @@ import Tooltip from '../../../../../ui/tooltip/tooltip';
import { I18nContext } from '../../../../../../contexts/i18n';
import { useCopyToClipboard } from '../../../../../../hooks/useCopyToClipboard';
import {
Icon,
ICON_NAMES,
} from '../../../../../component-library/icon/deprecated';
import { Icon, IconName } from '../../../../../component-library';
import { IconColor } from '../../../../../../helpers/constants/design-system';
const CopyRawData = ({ data }) => {
@ -25,7 +22,7 @@ const CopyRawData = ({ data }) => {
>
<div className="copy-raw-data__icon">
<Icon
name={copied ? ICON_NAMES.COPY_SUCCESS : ICON_NAMES.COPY}
name={copied ? IconName.CopySuccess : IconName.Copy}
color={IconColor.iconDefault}
/>
</div>

View File

@ -95,7 +95,7 @@ const EthOverview = ({ className }) => {
/>
}
className={className}
icon={<Identicon diameter={32} image={primaryTokenImage} imageBorder />}
icon={<Identicon diameter={32} image={primaryTokenImage} />}
/>
);
};

View File

@ -73,6 +73,12 @@ function getActionFunctionById(id, history) {
updateViewedNotifications({ 19: true });
history.push(`${EXPERIMENTAL_ROUTE}#autodetect-nfts`);
},
20: () => {
updateViewedNotifications({ 20: true });
global.platform.openTab({
url: ZENDESK_URLS.LEDGER_FIREFOX_U2F_GUIDE,
});
},
};
return actionFunctions[id];

View File

@ -1,8 +1,38 @@
# Component Library
**⚠️ THESE COMPONENTS HAVE BREAKING CHANGES ⚠️**
This folder contains design system components that are built 1:1 with the Figma [DS Components](https://www.figma.com/file/HKpPKij9V3TpsyMV1TpV7C/DS-Components?node-id=16-6) UI kit and should be used where possible in all UI feature work.
This folder contains new design system(`DS 2.0`) components that will be used for the IA/Nav redesign work. They are currently a WIP and should not be used for feature work. That should change in the near future but for the time being please use existing UI components in `ui/components/ui`
## Architecture
All components are built on top of the `Box` component and accept all `Box` [component props](https://metamask.github.io/metamask-storybook/?path=/docs/components-ui-box--default-story#props)
#### Layout
`component-library` components accept all utility props for layout
```
import { Text } from '../../component-library'
<Text marginBottom={4}>This text has a margin-bottom of 16px</Text>
```
#### Polymorphic `as` prop
`component-library` components accept a polymorphic as prop to change the root html element of a component
```
import { Text } from '../../component-library'
<ul>
<Text as="li">This renders as list item html element</Text>
</ul>
```
## TypeScript
We are currently in the process of migrating all component-library components to TypeScript. Feel free to contribute by creating a PR against one of [these issues](https://github.com/MetaMask/metamask-extension/issues?q=is%3Aissue+is%3Aopen+DS%2FExtension%2F2023%2FQ2%2FO1%2FKR3)
## Support
If internal folks have any questions please reach out the design system team via the internal slack channel [#metamask-design-system](https://consensys.slack.com/archives/C0354T27M5M) 💁

View File

@ -59,7 +59,7 @@ The `AvatarBase` component can contain images, icons or text
```jsx
import { AvatarBase } from '../../component-library';
<AvatarBase>
<img src="./images/eth_logo.svg" />
<img src="./images/eth_logo.png" />
</AvatarBase>
<AvatarBase>
<img width="100%" src="./images/arbitrum.svg" />

View File

@ -6,6 +6,7 @@ import {
BackgroundColor,
BorderColor,
TextColor,
IconColor,
DISPLAY,
JustifyContent,
AlignItems,
@ -81,7 +82,10 @@ AvatarBase.propTypes = {
* The color of the text inside the AvatarBase
* Defaults to TextColor.textDefault
*/
color: PropTypes.oneOf(Object.values(TextColor)),
color: PropTypes.oneOf([
...Object.values(TextColor),
...Object.values(IconColor),
]),
/**
* Additional classNames to be added to the AvatarToken
*/

View File

@ -110,7 +110,7 @@ export const Size = (args) => (
export const Children = (args) => (
<Box display={DISPLAY.FLEX} gap={1}>
<AvatarBase {...args}>
<img src="./images/eth_logo.svg" />
<img src="./images/eth_logo.png" />
</AvatarBase>
<AvatarBase {...args}>
<img width="100%" src="./images/arbitrum.svg" />

View File

@ -6,7 +6,7 @@ import { AvatarFavicon, AVATAR_FAVICON_SIZES } from '.';
describe('AvatarFavicon', () => {
const args = {
src: './images/eth_logo.svg',
src: './images/eth_logo.png',
name: 'test',
};

View File

@ -73,7 +73,10 @@ AvatarIcon.propTypes = {
* The color of the text inside the AvatarIcon
* Defaults to TextColor.primaryDefault
*/
color: PropTypes.oneOf(Object.values(TextColor)),
color: PropTypes.oneOf([
...Object.values(TextColor),
...Object.values(IconColor),
]),
/**
* Additional classNames to be added to the AvatarIcon
*/

View File

@ -13,7 +13,7 @@ import { AvatarNetwork } from './avatar-network';
describe('AvatarNetwork', () => {
const args = {
name: 'ethereum',
src: './images/eth_logo.svg',
src: './images/eth_logo.png',
showHalo: false,
};

View File

@ -72,7 +72,7 @@ Use the `src` prop to set the image to be rendered of the `AvatarToken`.
```jsx
import { AvatarToken } from '../../component-library';
<AvatarToken src="./images/eth_logo.svg" />
<AvatarToken src="./images/eth_logo.png" />
<AvatarToken src="./images/arbitrum.svg" />
<AvatarToken src="./images/bnb.png" />
<AvatarToken src="https://static.metaswap.codefi.network/api/v1/tokenIcons/1/0x6b175474e89094c44da98b954eedeac495271d0f.png" />
@ -92,7 +92,7 @@ Use the `showHalo` prop to display the component with halo effect. Only works if
```jsx
import { AvatarToken } from '../../component-library';
<AvatarToken src="./images/eth_logo.svg" showHalo />;
<AvatarToken src="./images/eth_logo.png" showHalo />;
```
### Color, Background Color And Border Color

View File

@ -61,7 +61,7 @@ export default {
},
args: {
name: 'eth',
src: './images/eth_logo.svg',
src: './images/eth_logo.png',
size: Size.MD,
showHalo: false,
},
@ -126,7 +126,7 @@ export const SizeStory = (args) => (
<BadgeWrapper
badge={
<AvatarNetwork
src="./images/eth_logo.svg"
src="./images/eth_logo.png"
name="ETH"
size={Size.XS}
borderColor={BackgroundColor.backgroundDefault}
@ -139,7 +139,7 @@ export const SizeStory = (args) => (
<BadgeWrapper
badge={
<AvatarNetwork
src="./images/eth_logo.svg"
src="./images/eth_logo.png"
name="ETH"
size={Size.XS}
borderColor={BackgroundColor.backgroundDefault}
@ -152,7 +152,7 @@ export const SizeStory = (args) => (
<BadgeWrapper
badge={
<AvatarNetwork
src="./images/eth_logo.svg"
src="./images/eth_logo.png"
name="ETH"
size={Size.XS}
borderColor={BackgroundColor.backgroundDefault}
@ -165,7 +165,7 @@ export const SizeStory = (args) => (
<BadgeWrapper
badge={
<AvatarNetwork
src="./images/eth_logo.svg"
src="./images/eth_logo.png"
name="ETH"
size={Size.XS}
borderColor={BackgroundColor.backgroundDefault}
@ -178,7 +178,7 @@ export const SizeStory = (args) => (
<BadgeWrapper
badge={
<AvatarNetwork
src="./images/eth_logo.svg"
src="./images/eth_logo.png"
name="ETH"
size={Size.SM}
borderColor={BackgroundColor.backgroundDefault}
@ -297,7 +297,7 @@ Name.args = {
export const Src = (args) => (
<Box display={DISPLAY.FLEX} gap={1}>
<AvatarToken {...args} src="./images/eth_logo.svg" />
<AvatarToken {...args} src="./images/eth_logo.png" />
<AvatarToken {...args} src="./images/arbitrum.svg" />
<AvatarToken {...args} src="./images/bnb.png" />
<AvatarToken

View File

@ -62,7 +62,7 @@ import {
>
<AvatarToken
name="Eth"
src="./images/eth_logo.svg"
src="./images/eth_logo.png"
borderColor={BorderColor.borderMuted}
/>
</BadgeWrapper>

View File

@ -114,7 +114,7 @@ export const Children: ComponentStory<typeof BadgeWrapper> = () => (
>
<AvatarToken
name="Eth"
src="./images/eth_logo.svg"
src="./images/eth_logo.png"
borderColor={BorderColor.borderMuted}
/>
</BadgeWrapper>

View File

@ -14,7 +14,7 @@ import {
AlignItems,
BackgroundColor,
TextVariant,
TEXT_ALIGN,
TextAlign,
} from '../../../helpers/constants/design-system';
import { HeaderBase } from './header-base';
import README from './README.mdx';
@ -37,7 +37,7 @@ export const DefaultStory = Template.bind({});
DefaultStory.args = {
children: (
<Text variant={TextVariant.headingSm} textAlign={TEXT_ALIGN.CENTER}>
<Text variant={TextVariant.headingSm} textAlign={TextAlign.Center}>
Title is sentence case no period
</Text>
),
@ -62,7 +62,7 @@ DefaultStory.storyName = 'Default';
export const Children = (args) => {
return (
<HeaderBase {...args}>
<Text variant={TextVariant.headingSm} textAlign={TEXT_ALIGN.CENTER}>
<Text variant={TextVariant.headingSm} textAlign={TextAlign.Center}>
Title is sentence case no period
</Text>
</HeaderBase>
@ -82,7 +82,7 @@ export const StartAccessory = (args) => {
}
{...args}
>
<Text variant={TextVariant.headingSm} textAlign={TEXT_ALIGN.CENTER}>
<Text variant={TextVariant.headingSm} textAlign={TextAlign.Center}>
Title is sentence case no period
</Text>
</HeaderBase>
@ -102,7 +102,7 @@ export const EndAccessory = (args) => {
}
{...args}
>
<Text variant={TextVariant.headingSm} textAlign={TEXT_ALIGN.CENTER}>
<Text variant={TextVariant.headingSm} textAlign={TextAlign.Center}>
Title is sentence case no period
</Text>
</HeaderBase>
@ -116,7 +116,7 @@ export const UseCaseDemos = (args) => (
<HeaderBase marginBottom={4} {...args}>
<Text
variant={TextVariant.headingSm}
textAlign={TEXT_ALIGN.CENTER}
textAlign={TextAlign.Center}
backgroundColor={BackgroundColor.primaryAlternative}
>
Title is sentence case no period
@ -139,7 +139,7 @@ export const UseCaseDemos = (args) => (
>
<Text
variant={TextVariant.headingSm}
textAlign={TEXT_ALIGN.CENTER}
textAlign={TextAlign.Center}
backgroundColor={BackgroundColor.primaryAlternative}
>
Title is sentence case no period
@ -162,7 +162,7 @@ export const UseCaseDemos = (args) => (
>
<Text
variant={TextVariant.headingSm}
textAlign={TEXT_ALIGN.CENTER}
textAlign={TextAlign.Center}
backgroundColor={BackgroundColor.primaryAlternative}
>
Title is sentence case no period
@ -193,7 +193,7 @@ export const UseCaseDemos = (args) => (
>
<Text
variant={TextVariant.headingSm}
textAlign={TEXT_ALIGN.CENTER}
textAlign={TextAlign.Center}
backgroundColor={BackgroundColor.primaryAlternative}
>
Title is sentence case no period
@ -225,7 +225,7 @@ export const UseCaseDemos = (args) => (
>
<Text
variant={TextVariant.headingSm}
textAlign={TEXT_ALIGN.CENTER}
textAlign={TextAlign.Center}
backgroundColor={BackgroundColor.primaryAlternative}
>
Title is sentence case no period
@ -260,7 +260,7 @@ export const UseCaseDemos = (args) => (
>
<Text
variant={TextVariant.headingSm}
textAlign={TEXT_ALIGN.CENTER}
textAlign={TextAlign.Center}
backgroundColor={BackgroundColor.primaryAlternative}
>
Title is sentence case no period

View File

@ -39,3 +39,4 @@ export { ModalOverlay } from './modal-overlay';
export { BannerBase } from './banner-base';
export { BannerAlert, BANNER_ALERT_SEVERITIES } from './banner-alert';
export { BannerTip, BannerTipLogoType } from './banner-tip';
export { PopoverHeader } from './popover-header';

View File

@ -0,0 +1,98 @@
import { Story, Canvas, ArgsTable } from '@storybook/addon-docs';
import { PopoverHeader } from './popover-header';
# PopoverHeader
PopoverHeader is built on top of [HeaderBase](/docs/components-componentlibrary-headerbase--default-story) component with the most common use case of a back button in the startAccessory position, title, and close button in the endAccessory position.
<Canvas>
<Story id="components-componentlibrary-popoverheader--default-story" />
</Canvas>
## Props
The `PopoverHeader` accepts all props below as well as all [Box](/docs/ui-components-ui-box-box-stories-js--default-story#props) component props
<ArgsTable of={PopoverHeader} />
### Children
Wrapping string content in the `PopoverHeader` component will be rendered in the center of the header with the default title `Text` component.
<Canvas>
<Story id="components-componentlibrary-popoverheader--children" />
</Canvas>
```jsx
import { PopoverHeader } from '../../component-library';
<PopoverHeader>Title is sentence case no period</PopoverHeader>;
```
### onBack
Use the onClick handler `onBack` prop to render the `ButtonIcon` back button in the startAccessory position.
Use the `backButtonProps` prop to pass additional props to the `ButtonIcon` back button.
<Canvas>
<Story id="components-componentlibrary-popoverheader--on-back" />
</Canvas>
```jsx
import { PopoverHeader } from '../../component-library';
<PopoverHeader onBack={() => console.log('Back button click')}>
OnBack Demo
</PopoverHeader>;
```
### onClose
Use the onClick handler `onClose` prop to render the `ButtonIcon` back button in the endAccessory position.
Use the `backButtonProps` prop to pass additional props to the `ButtonIcon` back button.
<Canvas>
<Story id="components-componentlibrary-popoverheader--on-close" />
</Canvas>
```jsx
import { PopoverHeader } from '../../component-library';
<PopoverHeader onClose={() => console.log('Back button click')}>
OnClose Demo
</PopoverHeader>;
```
### startAccessory
Use the `startAccessory` prop to render a component in the startAccessory position. This will override the default back `ButtonIcon`.
<Canvas>
<Story id="components-componentlibrary-popoverheader--start-accessory" />
</Canvas>
```jsx
import { PopoverHeader, Button, BUTTON_SIZES } from '../../component-library';
<PopoverHeader startAccessory={<Button size={BUTTON_SIZES.SM}>Demo</Button>}>
StartAccessory
</PopoverHeader>;
```
### endAccessory
Use the `endAccessory` prop to render a component in the endAccessory position. This will override the default close `ButtonIcon`.
<Canvas>
<Story id="components-componentlibrary-popoverheader--end-accessory" />
</Canvas>
```jsx
import { PopoverHeader, Button, BUTTON_SIZES } from '../../component-library';
<PopoverHeader endAccessory={<Button size={BUTTON_SIZES.SM}>Demo</Button>}>
EndAccessory
</PopoverHeader>;
```

View File

@ -0,0 +1,20 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`PopoverHeader should render PopoverHeader correctly 1`] = `
<div>
<div
class="box mm-header-base mm-popover-header box--display-flex box--flex-direction-row box--justify-content-space-between"
data-testid="popover-header"
>
<div
class="box mm-header-base__children box--flex-direction-row box--width-full"
>
<h4
class="box mm-text mm-text--heading-sm mm-text--text-align-center box--flex-direction-row box--color-text-default"
>
Popover Header
</h4>
</div>
</div>
</div>
`;

View File

@ -0,0 +1,2 @@
export { PopoverHeader } from './popover-header';
export type { PopoverHeaderProps } from './popover-header.types';

View File

@ -0,0 +1,65 @@
import React from 'react';
import { ComponentStory, ComponentMeta } from '@storybook/react';
import { BUTTON_SIZES, Button } from '..';
import { PopoverHeader } from './popover-header';
import README from './README.mdx';
export default {
title: 'Components/ComponentLibrary/PopoverHeader',
component: PopoverHeader,
parameters: {
docs: {
page: README,
},
},
argTypes: {
children: { control: 'text' },
className: { control: 'text' },
onBack: { action: 'onBack' },
onClose: { action: 'onClose' },
},
args: {
children: 'PopoverHeader',
},
} as ComponentMeta<typeof PopoverHeader>;
const Template: ComponentStory<typeof PopoverHeader> = (args) => {
return <PopoverHeader {...args}>PopoverHeader</PopoverHeader>;
};
export const DefaultStory = Template.bind({});
DefaultStory.storyName = 'Default';
export const Children: ComponentStory<typeof PopoverHeader> = (args) => (
<PopoverHeader {...args} />
);
Children.args = {
children: 'PopoverHeader Title',
};
export const OnBack: ComponentStory<typeof PopoverHeader> = (args) => (
<PopoverHeader {...args}>OnBack Demo</PopoverHeader>
);
export const OnClose: ComponentStory<typeof PopoverHeader> = (args) => (
<PopoverHeader {...args}>OnClose Demo</PopoverHeader>
);
export const StartAccessory: ComponentStory<typeof PopoverHeader> = (args) => (
<PopoverHeader
startAccessory={<Button size={BUTTON_SIZES.SM}>Demo</Button>}
{...args}
>
StartAccessory
</PopoverHeader>
);
export const EndAccessory: ComponentStory<typeof PopoverHeader> = (args) => (
<PopoverHeader
endAccessory={<Button size={BUTTON_SIZES.SM}>Demo</Button>}
{...args}
>
EndAccessory
</PopoverHeader>
);

View File

@ -0,0 +1,61 @@
/* eslint-disable jest/require-top-level-describe */
import { render, fireEvent } from '@testing-library/react';
import React from 'react';
import { PopoverHeader } from './popover-header';
describe('PopoverHeader', () => {
it('should render PopoverHeader correctly', () => {
const { getByTestId, container } = render(
<PopoverHeader data-testid="popover-header">
Popover Header
</PopoverHeader>,
);
expect(getByTestId('popover-header')).toHaveClass('mm-popover-header');
expect(container).toMatchSnapshot();
});
it('should render popover header title', () => {
const { getByText } = render(
<PopoverHeader data-testid="popover-header">
Popover Header Test
</PopoverHeader>,
);
expect(getByText('Popover Header Test')).toBeDefined();
});
it('should render popover header back button', () => {
const onBackTest = jest.fn();
const { getByTestId } = render(
<PopoverHeader
data-testid="popover"
onBack={onBackTest}
backButtonProps={{ 'data-testid': 'back' }}
>
Popover
</PopoverHeader>,
);
const backButton = getByTestId('back');
fireEvent.click(backButton);
expect(onBackTest).toHaveBeenCalled();
});
it('should render popover header close button', () => {
const onCloseTest = jest.fn();
const { getByTestId } = render(
<PopoverHeader
data-testid="popover"
onClose={onCloseTest}
closeButtonProps={{ 'data-testid': 'close' }}
>
Popover
</PopoverHeader>,
);
const closeButton = getByTestId('close');
fireEvent.click(closeButton);
expect(onCloseTest).toHaveBeenCalled();
});
});

View File

@ -0,0 +1,61 @@
import React from 'react';
import classnames from 'classnames';
import { HeaderBase, Text, ButtonIcon, ButtonIconSize, IconName } from '..';
import {
TextVariant,
TextAlign,
} from '../../../helpers/constants/design-system';
import { useI18nContext } from '../../../hooks/useI18nContext';
import { PopoverHeaderProps } from '.';
export const PopoverHeader: React.FC<PopoverHeaderProps> = ({
children,
className = '',
startAccessory,
endAccessory,
onClose,
closeButtonProps,
onBack,
backButtonProps,
...props
}) => {
const t = useI18nContext();
return (
<HeaderBase
className={classnames('mm-popover-header', className)}
startAccessory={
startAccessory ||
(onBack && (
<ButtonIcon
iconName={IconName.ArrowLeft}
ariaLabel={t('back')}
size={ButtonIconSize.Sm}
onClick={onBack}
{...backButtonProps}
/>
))
}
endAccessory={
endAccessory ||
(onClose && (
<ButtonIcon
iconName={IconName.Close}
ariaLabel={t('close')}
size={ButtonIconSize.Sm}
onClick={onClose}
{...closeButtonProps}
/>
))
}
{...props}
>
{typeof children === 'string' ? (
<Text variant={TextVariant.headingSm} textAlign={TextAlign.Center}>
{children}
</Text>
) : (
children
)}
</HeaderBase>
);
};

View File

@ -0,0 +1,42 @@
import React from 'react';
import type { ButtonIconProps } from '../button-icon/button-icon.types';
import type { HeaderBaseProps } from '../header-base';
export interface PopoverHeaderProps extends HeaderBaseProps {
/**
* The contents within the PopoverHeader positioned middle (popular for title use case)
*/
children?: React.ReactNode;
/**
* Additional classNames to be added to the Popover component
*/
className?: string;
/**
* The onClick handler for the back `ButtonIcon`
* When passed this will allow for the back `ButtonIcon` to show
*/
onBack?: () => void;
/**
* The props to pass to the back `ButtonIcon`
*/
backButtonProps?: ButtonIconProps;
/**
* The start (left) content area of PopoverHeader
* Default to have the back `ButtonIcon` when `onBack` is passed, but passing a `startAccessory` will override this
*/
startAccessory?: React.ReactNode;
/**
* The onClick handler for the close `ButtonIcon`
* When passed this will allow for the close `ButtonIcon` to show
*/
onClose?: () => void;
/**
* The props to pass to the close `ButtonIcon`
*/
closeButtonProps?: ButtonIconProps;
/**
* The end (right) content area of PopoverHeader
* Default to have the close `ButtonIcon` when `onClose` is passed, but passing a `endAccessory` will override this
*/
endAccessory?: React.ReactNode;
}

View File

@ -277,7 +277,7 @@ export const StartAccessoryEndAccessory = (args) => {
>
<AvatarToken
name="eth"
src="./images/eth_logo.svg"
src="./images/eth_logo.png"
size={Size.SM}
/>
<Text>ETH</Text>

View File

@ -28,7 +28,7 @@ exports[`MultichainTokenListItem should render correctly 1`] = `
<img
alt="undefined logo"
class="mm-avatar-network__network-image"
src="./images/eth_logo.svg"
src="./images/eth_logo.png"
/>
</div>
</div>

View File

@ -30,7 +30,7 @@ export default {
args: {
secondary: '$9.80 USD',
primary: '88.00687889',
tokenImage: './images/eth_logo.svg',
tokenImage: './images/eth_logo.png',
tokenSymbol: 'ETH',
title: 'Ethereum',
},

View File

@ -40,7 +40,7 @@ WithImage.args = {
addBorder: false,
diameter: 32,
useBlockie: false,
image: './images/eth_logo.svg',
image: './images/eth_logo.png',
alt: 'Ethereum',
imageBorder: true,
};

View File

@ -31,7 +31,7 @@ DefaultStory.storyName = 'Default';
DefaultStory.args = {
name: 'eth',
icon: './images/eth_logo.svg',
icon: './images/eth_logo.png',
size: 24,
};

View File

@ -6,7 +6,7 @@ describe('SiteIcon', () => {
const args = {
size: 32,
name: 'Snap name',
icon: './images/eth_logo.svg',
icon: './images/eth_logo.png',
className: 'classname-test',
fallbackClassName: 'fallback-classname-test',
};

View File

@ -15,6 +15,8 @@ const ZENDESK_URLS = {
'https://metamask.zendesk.com/hc/en-us/articles/360015289932',
INFURA_BLOCKAGE:
'https://metamask.zendesk.com/hc/en-us/articles/360059386712',
LEDGER_FIREFOX_U2F_GUIDE:
'https://support.ledger.com/hc/en-us/articles/10371387758493-MetaMask-Firefox-Ledger-Integration-Issue?support=true',
LEGACY_WEB3: 'https://metamask.zendesk.com/hc/en-us/articles/360053147012',
NFT_TOKENS:
'https://metamask.zendesk.com/hc/en-us/articles/360058238591-NFT-tokens-in-MetaMask-wallet',

View File

@ -261,17 +261,15 @@ export async function getAssetDetails(
// if we can't determine any token standard or details return the data we can extract purely from the parsed transaction data
return { toAddress, tokenId };
}
const tokenValue = getTokenValueParam(tokenData);
const tokenDecimals = tokenDetails?.decimals;
const tokenAmount =
tokenData &&
tokenDetails?.decimals &&
calcTokenAmount(
getTokenValueParam(tokenData),
tokenDetails?.decimals,
).toString(10);
tokenValue &&
tokenDecimals &&
calcTokenAmount(tokenValue, tokenDecimals).toString(10);
const decimals =
tokenDetails?.decimals && Number(tokenDetails.decimals?.toString(10));
const decimals = tokenDecimals && Number(tokenDecimals?.toString(10));
if (tokenDetails?.standard === TokenStandard.ERC20) {
tokenId = undefined;

View File

@ -37,12 +37,16 @@ describe('useAssetDetails', () => {
let getTokenStandardAndDetailsStub;
beforeEach(() => {
getTokenStandardAndDetailsStub = jest
.spyOn(Actions, 'getTokenStandardAndDetails')
.mockImplementation(() => Promise.resolve({}));
getTokenStandardAndDetailsStub = jest.spyOn(
Actions,
'getTokenStandardAndDetails',
);
});
it('should return object with tokenSymbol set to an empty string, when getAssetDetails returns and empty object', async () => {
getTokenStandardAndDetailsStub.mockImplementation(() =>
Promise.resolve({}),
);
const toAddress = '000000000000000000000000000000000000dead';
const tokenAddress = '0x1';
@ -106,6 +110,46 @@ describe('useAssetDetails', () => {
});
});
it('should return object with correct tokenValues for an ERC20 token with no decimals', async () => {
const userAddress = '0xf04a5cc80b1e94c69b48f5ee68a08cd2f09a7c3e';
const tokenAddress = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2';
const toAddress = '000000000000000000000000000000000000dead';
const transactionData = `0xa9059cbb000000000000000000000000${toAddress}00000000000000000000000000000000000000000000000000000000000001f4`;
const standard = TokenStandard.ERC20;
const symbol = 'WETH';
const balance = '1';
getTokenStandardAndDetailsStub.mockImplementation(() =>
Promise.resolve({
standard,
balance,
symbol,
}),
);
const { result, waitForNextUpdate } = renderUseAssetDetails({
tokenAddress,
userAddress,
transactionData,
});
await waitForNextUpdate();
expect(result.current).toStrictEqual({
assetAddress: tokenAddress,
assetName: undefined,
assetStandard: standard,
toAddress: `0x${toAddress}`,
tokenAmount: undefined,
tokenId: undefined,
tokenImage: undefined,
tokenSymbol: symbol,
userBalance: balance,
decimals: undefined,
});
});
it('should return object with correct tokenValues for an ERC721 token', async () => {
const tokenAddress = '0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D';
const toAddress = '000000000000000000000000000000000000dead';

View File

@ -1,107 +1,65 @@
/* eslint-disable react/prop-types */
import React, { useEffect } from 'react';
import { text } from '@storybook/addon-knobs';
import { store, getNewState } from '../../../.storybook/preview';
import React from 'react';
import { Provider } from 'react-redux';
import { suggestedAssets as mockSuggestedAssets } from '../../../.storybook/initial-states/approval-screens/add-suggested-token';
import { updateMetamaskState } from '../../store/actions';
import configureStore from '../../store/store';
import mockState from '../../../.storybook/test-data';
import ConfirmAddSuggestedToken from '.';
const store = configureStore({
metamask: {
...mockState.metamask,
suggestedAssets: [...mockSuggestedAssets],
tokens: [],
},
});
export default {
title: 'Pages/ConfirmAddSuggestedToken',
argTypes: {
tokens: {
control: 'array',
table: { category: 'Data' },
},
suggestedAssets: {
control: 'array',
table: { category: 'Data' },
},
},
decorators: [(story) => <Provider store={store}>{story()}</Provider>],
};
const { metamask: state } = store.getState();
const PageSet = ({ children, suggestedAssets, tokens }) => {
const symbol = text('symbol', 'META');
const image = text('Icon URL', 'metamark.svg');
useEffect(() => {
if (!suggestedAssets?.length) {
return;
}
suggestedAssets[0].asset.image = image;
suggestedAssets[0].asset.symbol = symbol;
store.dispatch(
updateMetamaskState(
getNewState(state, {
suggestedAssets,
}),
),
);
}, [image, suggestedAssets, symbol]);
useEffect(() => {
store.dispatch(
updateMetamaskState(
getNewState(state, {
tokens,
}),
),
);
}, [tokens]);
return children;
};
export const DefaultStory = ({ suggestedAssets, tokens }) => {
return (
<PageSet suggestedAssets={suggestedAssets} tokens={tokens}>
<ConfirmAddSuggestedToken />
</PageSet>
);
};
export const DefaultStory = () => <ConfirmAddSuggestedToken />;
DefaultStory.storyName = 'Default';
DefaultStory.args = {
suggestedAssets: [...mockSuggestedAssets],
tokens: [],
};
export const WithDuplicateAddress = ({ suggestedAssets, tokens }) => {
return (
<PageSet suggestedAssets={suggestedAssets} tokens={tokens}>
<ConfirmAddSuggestedToken />
</PageSet>
);
};
WithDuplicateAddress.args = {
suggestedAssets: [...mockSuggestedAssets],
tokens: [
{
...mockSuggestedAssets[0].asset,
},
],
};
export const WithDuplicateAddress = () => <ConfirmAddSuggestedToken />;
const WithDuplicateAddressStore = configureStore({
metamask: {
...mockState.metamask,
suggestedAssets: [...mockSuggestedAssets],
tokens: [
{
...mockSuggestedAssets[0].asset,
},
],
},
});
WithDuplicateAddress.decorators = [
(story) => <Provider store={WithDuplicateAddressStore}>{story()}</Provider>,
];
export const WithDuplicateSymbolAndDifferentAddress = ({
suggestedAssets,
tokens,
}) => {
return (
<PageSet suggestedAssets={suggestedAssets} tokens={tokens}>
<ConfirmAddSuggestedToken />
</PageSet>
);
};
WithDuplicateSymbolAndDifferentAddress.args = {
suggestedAssets: [...mockSuggestedAssets],
tokens: [
{
...mockSuggestedAssets[0].asset,
address: '0xNonSuggestedAddress',
},
],
};
export const WithDuplicateSymbolAndDifferentAddress = () => (
<ConfirmAddSuggestedToken />
);
const WithDuplicateSymbolAndDifferentAddressStore = configureStore({
metamask: {
...mockState.metamask,
suggestedAssets: [...mockSuggestedAssets],
tokens: [
{
...mockSuggestedAssets[0].asset,
address: '0xNonSuggestedAddress',
},
],
},
});
WithDuplicateSymbolAndDifferentAddress.decorators = [
(story) => (
<Provider store={WithDuplicateSymbolAndDifferentAddressStore}>
{story()}
</Provider>
),
];

Some files were not shown because too many files have changed in this diff Show More