mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
17921 Update TransactionAlerts with BannerAlert (#17940)
* use banner alerts * update selector for banner alert content * lintage * update button click for banner alert structure * bump global branches coverage target * removed unnecessary Typography usage * remove Typography usage * transaction alerts story * pending transaction alerts * created separate stories for each alert scenario
This commit is contained in:
parent
a2838b0dd1
commit
e7527b65ee
@ -53,9 +53,7 @@ describe('Failing contract interaction ', function () {
|
|||||||
// display warning when transaction is expected to fail
|
// display warning when transaction is expected to fail
|
||||||
const warningText =
|
const warningText =
|
||||||
'We were not able to estimate gas. There might be an error in the contract and this transaction may fail.';
|
'We were not able to estimate gas. There might be an error in the contract and this transaction may fail.';
|
||||||
const warning = await driver.findElement(
|
const warning = await driver.findElement('.mm-banner-alert .mm-text');
|
||||||
'.actionable-message__message',
|
|
||||||
);
|
|
||||||
const confirmButton = await driver.findElement(
|
const confirmButton = await driver.findElement(
|
||||||
'[data-testid="page-container-footer-next"]',
|
'[data-testid="page-container-footer-next"]',
|
||||||
);
|
);
|
||||||
@ -65,7 +63,7 @@ describe('Failing contract interaction ', function () {
|
|||||||
// dismiss warning and confirm the transaction
|
// dismiss warning and confirm the transaction
|
||||||
await driver.clickElement({
|
await driver.clickElement({
|
||||||
text: 'I want to proceed anyway',
|
text: 'I want to proceed anyway',
|
||||||
tag: 'button',
|
tag: 'span',
|
||||||
});
|
});
|
||||||
await driver.clickElement({ text: 'Confirm', tag: 'button' });
|
await driver.clickElement({ text: 'Confirm', tag: 'button' });
|
||||||
await driver.waitUntilXWindowHandles(2);
|
await driver.waitUntilXWindowHandles(2);
|
||||||
@ -141,9 +139,7 @@ describe('Failing contract interaction on non-EIP1559 network', function () {
|
|||||||
// display warning when transaction is expected to fail
|
// display warning when transaction is expected to fail
|
||||||
const warningText =
|
const warningText =
|
||||||
'We were not able to estimate gas. There might be an error in the contract and this transaction may fail.';
|
'We were not able to estimate gas. There might be an error in the contract and this transaction may fail.';
|
||||||
const warning = await driver.findElement(
|
const warning = await driver.findElement('.mm-banner-alert .mm-text');
|
||||||
'.actionable-message__message',
|
|
||||||
);
|
|
||||||
const confirmButton = await driver.findElement(
|
const confirmButton = await driver.findElement(
|
||||||
'[data-testid="page-container-footer-next"]',
|
'[data-testid="page-container-footer-next"]',
|
||||||
);
|
);
|
||||||
@ -153,7 +149,7 @@ describe('Failing contract interaction on non-EIP1559 network', function () {
|
|||||||
// dismiss warning and confirm the transaction
|
// dismiss warning and confirm the transaction
|
||||||
await driver.clickElement({
|
await driver.clickElement({
|
||||||
text: 'I want to proceed anyway',
|
text: 'I want to proceed anyway',
|
||||||
tag: 'button',
|
tag: 'span',
|
||||||
});
|
});
|
||||||
await driver.clickElement({ text: 'Confirm', tag: 'button' });
|
await driver.clickElement({ text: 'Confirm', tag: 'button' });
|
||||||
await driver.waitUntilXWindowHandles(2);
|
await driver.waitUntilXWindowHandles(2);
|
||||||
|
@ -6,10 +6,9 @@ import { PriorityLevels } from '../../../../shared/constants/gas';
|
|||||||
import { submittedPendingTransactionsSelector } from '../../../selectors';
|
import { submittedPendingTransactionsSelector } from '../../../selectors';
|
||||||
import { useGasFeeContext } from '../../../contexts/gasFee';
|
import { useGasFeeContext } from '../../../contexts/gasFee';
|
||||||
import { useI18nContext } from '../../../hooks/useI18nContext';
|
import { useI18nContext } from '../../../hooks/useI18nContext';
|
||||||
import ActionableMessage from '../../ui/actionable-message/actionable-message';
|
import { BannerAlert, ButtonLink, Text } from '../../component-library';
|
||||||
import SimulationErrorMessage from '../../ui/simulation-error-message';
|
import SimulationErrorMessage from '../../ui/simulation-error-message';
|
||||||
import Typography from '../../ui/typography';
|
import { SEVERITIES } from '../../../helpers/constants/design-system';
|
||||||
import { TypographyVariant } from '../../../helpers/constants/design-system';
|
|
||||||
import ZENDESK_URLS from '../../../helpers/constants/zendesk-url';
|
import ZENDESK_URLS from '../../../helpers/constants/zendesk-url';
|
||||||
|
|
||||||
const TransactionAlerts = ({
|
const TransactionAlerts = ({
|
||||||
@ -30,15 +29,8 @@ const TransactionAlerts = ({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{supportsEIP1559 && pendingTransactions?.length > 0 && (
|
{supportsEIP1559 && pendingTransactions?.length > 0 && (
|
||||||
<ActionableMessage
|
<BannerAlert severity={SEVERITIES.WARNING}>
|
||||||
message={
|
<Text as="p">
|
||||||
<Typography
|
|
||||||
align="left"
|
|
||||||
className="transaction-alerts__pending-transactions"
|
|
||||||
margin={0}
|
|
||||||
tag={TypographyVariant.paragraph}
|
|
||||||
variant={TypographyVariant.H7}
|
|
||||||
>
|
|
||||||
<strong>
|
<strong>
|
||||||
{pendingTransactions?.length === 1
|
{pendingTransactions?.length === 1
|
||||||
? t('pendingTransactionSingle', [pendingTransactions?.length])
|
? t('pendingTransactionSingle', [pendingTransactions?.length])
|
||||||
@ -48,56 +40,30 @@ const TransactionAlerts = ({
|
|||||||
</strong>{' '}
|
</strong>{' '}
|
||||||
{t('pendingTransactionInfo')}
|
{t('pendingTransactionInfo')}
|
||||||
{t('learnCancelSpeeedup', [
|
{t('learnCancelSpeeedup', [
|
||||||
<a
|
<ButtonLink
|
||||||
key="cancelSpeedUpInfo"
|
key="cancelSpeedUpInfo"
|
||||||
href={ZENDESK_URLS.SPEEDUP_CANCEL}
|
href={ZENDESK_URLS.SPEEDUP_CANCEL}
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
{t('cancelSpeedUp')}
|
{t('cancelSpeedUp')}
|
||||||
</a>,
|
</ButtonLink>,
|
||||||
])}
|
])}
|
||||||
</Typography>
|
</Text>
|
||||||
}
|
</BannerAlert>
|
||||||
useIcon
|
|
||||||
iconFillColor="var(--color-warning-default)"
|
|
||||||
type="warning"
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
{estimateUsed === PriorityLevels.low && (
|
{estimateUsed === PriorityLevels.low && (
|
||||||
<ActionableMessage
|
<BannerAlert
|
||||||
dataTestId="low-gas-fee-alert"
|
data-testid="low-gas-fee-alert"
|
||||||
message={
|
severity={SEVERITIES.WARNING}
|
||||||
<Typography
|
|
||||||
align="left"
|
|
||||||
margin={0}
|
|
||||||
tag={TypographyVariant.paragraph}
|
|
||||||
variant={TypographyVariant.H7}
|
|
||||||
>
|
>
|
||||||
{t('lowPriorityMessage')}
|
{t('lowPriorityMessage')}
|
||||||
</Typography>
|
</BannerAlert>
|
||||||
}
|
|
||||||
useIcon
|
|
||||||
iconFillColor="var(--color-warning-default)"
|
|
||||||
type="warning"
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
{supportsEIP1559 && isNetworkBusy ? (
|
{supportsEIP1559 && isNetworkBusy ? (
|
||||||
<ActionableMessage
|
<BannerAlert severity={SEVERITIES.WARNING}>
|
||||||
message={
|
|
||||||
<Typography
|
|
||||||
align="left"
|
|
||||||
margin={0}
|
|
||||||
tag={TypographyVariant.paragraph}
|
|
||||||
variant={TypographyVariant.H7}
|
|
||||||
>
|
|
||||||
{t('networkIsBusy')}
|
{t('networkIsBusy')}
|
||||||
</Typography>
|
</BannerAlert>
|
||||||
}
|
|
||||||
iconFillColor="var(--color-warning-default)"
|
|
||||||
type="warning"
|
|
||||||
useIcon
|
|
||||||
/>
|
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -0,0 +1,167 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Provider } from 'react-redux';
|
||||||
|
import { keccak } from 'ethereumjs-util';
|
||||||
|
import { cloneDeep } from 'lodash';
|
||||||
|
import { GasFeeContextProvider } from '../../../contexts/gasFee';
|
||||||
|
import configureStore from '../../../store/store';
|
||||||
|
import testData from '../../../../.storybook/test-data';
|
||||||
|
import TransactionAlerts from '.';
|
||||||
|
|
||||||
|
const customTransaction = ({
|
||||||
|
estimateUsed,
|
||||||
|
hasSimulationError,
|
||||||
|
i = 0,
|
||||||
|
...props
|
||||||
|
} = {}) => {
|
||||||
|
const tx = {
|
||||||
|
simulationFails: Boolean(hasSimulationError),
|
||||||
|
userFeeLevel: estimateUsed ? 'low' : 'medium',
|
||||||
|
blockNumber: `${10902987 + i}`,
|
||||||
|
id: 4678200543090545 + i,
|
||||||
|
metamaskNetworkId: testData?.metamask?.network,
|
||||||
|
chainId: testData?.metamask?.provider?.chainId,
|
||||||
|
status: 'confirmed',
|
||||||
|
time: 1600654021000,
|
||||||
|
txParams: {
|
||||||
|
from: '0x64a845a5b02460acf8a3d84503b0d68d028b4bb4',
|
||||||
|
gas: '0x5208',
|
||||||
|
gasPrice: '0x147d357000',
|
||||||
|
nonce: '0xf',
|
||||||
|
to: testData?.metamask?.selectedAddress,
|
||||||
|
value: '0x63eb89da4ed00000',
|
||||||
|
...props?.txParams,
|
||||||
|
},
|
||||||
|
// '0x50be62ab1cabd03ff104c602c11fdef7a50f3d73c55006d5583ba97950ab1144',
|
||||||
|
transactionCategory: 'incoming',
|
||||||
|
...props,
|
||||||
|
};
|
||||||
|
// just simulate hash if not provided
|
||||||
|
if (!props?.hash) {
|
||||||
|
tx.hash = `0x${keccak(Buffer.from(JSON.stringify(tx))).toString('hex')}`;
|
||||||
|
}
|
||||||
|
return tx;
|
||||||
|
};
|
||||||
|
|
||||||
|
// simulate gas fee state
|
||||||
|
const customStore = ({
|
||||||
|
supportsEIP1559,
|
||||||
|
isNetworkBusy,
|
||||||
|
pendingCount = 0,
|
||||||
|
} = {}) => {
|
||||||
|
const data = cloneDeep({
|
||||||
|
...testData,
|
||||||
|
metamask: {
|
||||||
|
...testData?.metamask,
|
||||||
|
// isNetworkBusy
|
||||||
|
gasFeeEstimates: {
|
||||||
|
...testData?.metamask?.gasFeeEstimates,
|
||||||
|
networkCongestion: isNetworkBusy ? 1 : 0.1,
|
||||||
|
},
|
||||||
|
// supportsEIP1559
|
||||||
|
networkDetails: {
|
||||||
|
...testData?.metamask?.networkDetails,
|
||||||
|
EIPS: {
|
||||||
|
...testData?.metamask?.networkDetails?.EIPS,
|
||||||
|
1159: Boolean(supportsEIP1559),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// pendingTransactions
|
||||||
|
featureFlags: {
|
||||||
|
...testData?.metamask?.featureFlags,
|
||||||
|
showIncomingTransactions: pendingCount > 0,
|
||||||
|
},
|
||||||
|
incomingTransactions: {
|
||||||
|
...testData?.metamask?.incomingTransactions,
|
||||||
|
...Object.fromEntries(
|
||||||
|
Array.from({ length: pendingCount }).map((_, i) => {
|
||||||
|
const transaction = customTransaction({ i, status: 'submitted' });
|
||||||
|
return [transaction?.hash, transaction];
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return configureStore(data);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Components/App/TransactionAlerts',
|
||||||
|
argTypes: {
|
||||||
|
userAcknowledgedGasMissing: {
|
||||||
|
control: 'boolean',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
args: {
|
||||||
|
userAcknowledgedGasMissing: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// show everything
|
||||||
|
export const DefaultStory = (args) => (
|
||||||
|
<Provider
|
||||||
|
store={customStore({
|
||||||
|
isNetworkBusy: true,
|
||||||
|
supportsEIP1559: true,
|
||||||
|
pendingCount: 1,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<GasFeeContextProvider
|
||||||
|
transaction={customTransaction({
|
||||||
|
hasSimulationError: true,
|
||||||
|
estimateUsed: true,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<TransactionAlerts {...args} />
|
||||||
|
</GasFeeContextProvider>
|
||||||
|
</Provider>
|
||||||
|
);
|
||||||
|
DefaultStory.storyName = 'Default';
|
||||||
|
|
||||||
|
export const SimulationError = (args) => (
|
||||||
|
<Provider store={customStore({ supportsEIP1559: true })}>
|
||||||
|
<GasFeeContextProvider
|
||||||
|
transaction={customTransaction({ hasSimulationError: true })}
|
||||||
|
>
|
||||||
|
<TransactionAlerts {...args} />
|
||||||
|
</GasFeeContextProvider>
|
||||||
|
</Provider>
|
||||||
|
);
|
||||||
|
SimulationError.storyName = 'SimulationError';
|
||||||
|
|
||||||
|
export const SinglePendingTransaction = (args) => (
|
||||||
|
<Provider store={customStore({ supportsEIP1559: true, pendingCount: 1 })}>
|
||||||
|
<GasFeeContextProvider transaction={customTransaction()}>
|
||||||
|
<TransactionAlerts {...args} />
|
||||||
|
</GasFeeContextProvider>
|
||||||
|
</Provider>
|
||||||
|
);
|
||||||
|
SinglePendingTransaction.storyName = 'SinglePendingTransaction';
|
||||||
|
|
||||||
|
export const MultiplePendingTransactions = (args) => (
|
||||||
|
<Provider store={customStore({ supportsEIP1559: true, pendingCount: 2 })}>
|
||||||
|
<GasFeeContextProvider transaction={customTransaction()}>
|
||||||
|
<TransactionAlerts {...args} />
|
||||||
|
</GasFeeContextProvider>
|
||||||
|
</Provider>
|
||||||
|
);
|
||||||
|
MultiplePendingTransactions.storyName = 'MultiplePendingTransactions';
|
||||||
|
|
||||||
|
export const LowPriority = (args) => (
|
||||||
|
<Provider store={customStore()}>
|
||||||
|
<GasFeeContextProvider
|
||||||
|
transaction={customTransaction({ estimateUsed: true })}
|
||||||
|
>
|
||||||
|
<TransactionAlerts {...args} />
|
||||||
|
</GasFeeContextProvider>
|
||||||
|
</Provider>
|
||||||
|
);
|
||||||
|
LowPriority.storyName = 'LowPriority';
|
||||||
|
|
||||||
|
export const BusyNetwork = (args) => (
|
||||||
|
<Provider store={customStore({ isNetworkBusy: true })}>
|
||||||
|
<GasFeeContextProvider transaction={customTransaction()}>
|
||||||
|
<TransactionAlerts {...args} />
|
||||||
|
</GasFeeContextProvider>
|
||||||
|
</Provider>
|
||||||
|
);
|
||||||
|
BusyNetwork.storyName = 'BusyNetwork';
|
@ -1,6 +1,8 @@
|
|||||||
import React, { useContext } from 'react';
|
import React, { useContext } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import ActionableMessage from '../actionable-message';
|
import { BannerAlert } from '../../component-library';
|
||||||
|
import { SEVERITIES } from '../../../helpers/constants/design-system';
|
||||||
|
|
||||||
import { I18nContext } from '../../../../.storybook/i18n';
|
import { I18nContext } from '../../../../.storybook/i18n';
|
||||||
|
|
||||||
export default function SimulationErrorMessage({
|
export default function SimulationErrorMessage({
|
||||||
@ -9,21 +11,18 @@ export default function SimulationErrorMessage({
|
|||||||
}) {
|
}) {
|
||||||
const t = useContext(I18nContext);
|
const t = useContext(I18nContext);
|
||||||
|
|
||||||
return (
|
return userAcknowledgedGasMissing === true ? (
|
||||||
<ActionableMessage
|
<BannerAlert severity={SEVERITIES.DANGER}>
|
||||||
message={t('simulationErrorMessageV2')}
|
{t('simulationErrorMessageV2')}
|
||||||
useIcon
|
</BannerAlert>
|
||||||
iconFillColor="var(--color-error-default)"
|
) : (
|
||||||
type="danger"
|
<BannerAlert
|
||||||
primaryActionV2={
|
severity={SEVERITIES.DANGER}
|
||||||
userAcknowledgedGasMissing === true
|
actionButtonLabel={t('proceedWithTransaction')}
|
||||||
? undefined
|
actionButtonOnClick={setUserAcknowledgedGasMissing}
|
||||||
: {
|
>
|
||||||
label: t('proceedWithTransaction'),
|
{t('simulationErrorMessageV2')}
|
||||||
onClick: setUserAcknowledgedGasMissing,
|
</BannerAlert>
|
||||||
}
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user