EIP-1559 - Provide Updated UI for Dapp-Suggested Gas fees (#11523)

feature/default_network_editable
David Walsh 3 years ago committed by GitHub
parent e77101138a
commit d8984d3cf3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 16
      app/_locales/en/messages.json
  2. 13
      ui/components/app/edit-gas-display/edit-gas-display.component.js
  3. 6
      ui/components/app/edit-gas-display/index.scss
  4. 1
      ui/components/app/edit-gas-popover/edit-gas-popover.component.js
  5. 5
      ui/components/app/transaction-detail-item/index.scss
  6. 22
      ui/components/app/transaction-detail-item/transaction-detail-item.component.js
  7. 18
      ui/components/ui/actionable-message/actionable-message.js
  8. 2
      ui/components/ui/actionable-message/actionable-message.stories.js
  9. 14
      ui/components/ui/info-tooltip/index.scss
  10. 17
      ui/components/ui/info-tooltip/info-tooltip-icon.js
  11. 5
      ui/components/ui/info-tooltip/info-tooltip.js
  12. 42
      ui/pages/confirm-transaction-base/confirm-transaction-base.component.js
  13. 13
      ui/pages/swaps/fee-card/__snapshots__/fee-card.test.js.snap
  14. 10
      ui/pages/swaps/select-quote-popover/sort-list/__snapshots__/sort-list.test.js.snap
  15. 5
      ui/pages/swaps/select-quote-popover/sort-list/sort-list.test.js
  16. 5
      ui/pages/swaps/view-quote/__snapshots__/view-quote.test.js.snap
  17. 4
      ui/pages/swaps/view-quote/view-quote.test.js

@ -2442,9 +2442,25 @@
"transactionCreated": { "transactionCreated": {
"message": "Transaction created with a value of $1 at $2." "message": "Transaction created with a value of $1 at $2."
}, },
"transactionDetailDappGasHeading": {
"message": "$1 suggested gas fee",
"description": "$1 represents a dapp origin"
},
"transactionDetailDappGasTooltip": {
"message": "This gas fee suggestion is using legacy gas estimation which may be inaccurate."
},
"transactionDetailGasHeading": { "transactionDetailGasHeading": {
"message": "Estimated gas fee" "message": "Estimated gas fee"
}, },
"transactionDetailGasTooltipConversion": {
"message": "Learn more about gas fees"
},
"transactionDetailGasTooltipExplanation": {
"message": "Gas fees are set by the network and fluctuate based on network traffic and transaction complexity."
},
"transactionDetailGasTooltipIntro": {
"message": "Gas fees are paid to crypto miners who process transactions on the Ethereum network. MetaMask does not profit from gas fees."
},
"transactionDetailGasTotalSubtitle": { "transactionDetailGasTotalSubtitle": {
"message": "Amount + gas fee" "message": "Amount + gas fee"
}, },

@ -28,8 +28,7 @@ export default function EditGasDisplay({
alwaysShowForm = false, alwaysShowForm = false,
showEducationButton = false, showEducationButton = false,
onEducationClick, onEducationClick,
dappSuggestedGasFee = 0, transaction,
dappOrigin = '',
defaultEstimateToUse, defaultEstimateToUse,
maxPriorityFeePerGas, maxPriorityFeePerGas,
setMaxPriorityFeePerGas, setMaxPriorityFeePerGas,
@ -61,7 +60,7 @@ export default function EditGasDisplay({
const t = useContext(I18nContext); const t = useContext(I18nContext);
const requireDappAcknowledgement = Boolean( const requireDappAcknowledgement = Boolean(
dappSuggestedGasFee && !dappSuggestedGasFeeAcknowledged, transaction?.dappSuggestedGasFees && !dappSuggestedGasFeeAcknowledged,
); );
return ( return (
@ -79,7 +78,8 @@ export default function EditGasDisplay({
<div className="edit-gas-display__dapp-acknowledgement-warning"> <div className="edit-gas-display__dapp-acknowledgement-warning">
<ActionableMessage <ActionableMessage
className="actionable-message--warning" className="actionable-message--warning"
message={t('gasDisplayDappWarning', [dappOrigin])} message={t('gasDisplayDappWarning', [transaction.dappOrigin])}
iconFillColor="#f8c000"
useIcon useIcon
/> />
</div> </div>
@ -164,7 +164,7 @@ export default function EditGasDisplay({
onChange={setEstimateToUse} onChange={setEstimateToUse}
/> />
)} )}
{!alwaysShowForm && ( {!alwaysShowForm && !requireDappAcknowledgement && (
<button <button
className="edit-gas-display__advanced-button" className="edit-gas-display__advanced-button"
onClick={() => setShowAdvancedForm(!showAdvancedForm)} onClick={() => setShowAdvancedForm(!showAdvancedForm)}
@ -217,8 +217,6 @@ EditGasDisplay.propTypes = {
mode: PropTypes.oneOf(Object.values(EDIT_GAS_MODES)), mode: PropTypes.oneOf(Object.values(EDIT_GAS_MODES)),
showEducationButton: PropTypes.bool, showEducationButton: PropTypes.bool,
onEducationClick: PropTypes.func, onEducationClick: PropTypes.func,
dappSuggestedGasFee: PropTypes.number,
dappOrigin: PropTypes.string,
defaultEstimateToUse: PropTypes.oneOf(Object.values(GAS_RECOMMENDATIONS)), defaultEstimateToUse: PropTypes.oneOf(Object.values(GAS_RECOMMENDATIONS)),
maxPriorityFeePerGas: PropTypes.string, maxPriorityFeePerGas: PropTypes.string,
setMaxPriorityFeePerGas: PropTypes.func, setMaxPriorityFeePerGas: PropTypes.func,
@ -246,4 +244,5 @@ EditGasDisplay.propTypes = {
showAdvancedForm: PropTypes.bool, showAdvancedForm: PropTypes.bool,
setShowAdvancedForm: PropTypes.func, setShowAdvancedForm: PropTypes.func,
warning: PropTypes.string, warning: PropTypes.string,
transaction: PropTypes.object,
}; };

@ -2,6 +2,10 @@
& .actionable-message--warning, & .actionable-message--warning,
& .actionable-message--error { & .actionable-message--error {
margin-top: 0; margin-top: 0;
& .actionable-message__message {
text-align: start;
}
} }
&__top-tooltip { &__top-tooltip {
@ -22,7 +26,7 @@
} }
button.edit-gas-display__dapp-acknowledgement-button { button.edit-gas-display__dapp-acknowledgement-button {
margin: 0 auto; margin: 40px auto 0 auto;
display: block; display: block;
color: $secondary-1; color: $secondary-1;
border: 1px solid $secondary-1; border: 1px solid $secondary-1;

@ -220,6 +220,7 @@ export default function EditGasPopover({
isGasTooLow={isGasTooLow} isGasTooLow={isGasTooLow}
onEducationClick={() => setShowEducationContent(true)} onEducationClick={() => setShowEducationContent(true)}
mode={mode} mode={mode}
transaction={transaction}
{...editGasDisplayProps} {...editGasDisplayProps}
/> />
)} )}

@ -9,6 +9,11 @@
flex-grow: 1; flex-grow: 1;
} }
&__title--dapp-suggested {
color: $secondary-1;
font-weight: bold;
}
.info-tooltip { .info-tooltip {
display: inline-block; display: inline-block;
margin-inline-start: 4px; margin-inline-start: 4px;

@ -9,17 +9,18 @@ import {
} from '../../../helpers/constants/design-system'; } from '../../../helpers/constants/design-system';
export default function TransactionDetailItem({ export default function TransactionDetailItem({
detailTitle, detailTitle = '',
detailText, detailText = '',
detailTotal, detailTitleColor = COLORS.BLACK,
subTitle, detailTotal = '',
subText, subTitle = '',
subText = '',
}) { }) {
return ( return (
<div className="transaction-detail-item"> <div className="transaction-detail-item">
<div className="transaction-detail-item__row"> <div className="transaction-detail-item__row">
<Typography <Typography
color={COLORS.BLACK} color={detailTitleColor}
fontWeight={FONT_WEIGHT.BOLD} fontWeight={FONT_WEIGHT.BOLD}
variant={TYPOGRAPHY.H6} variant={TYPOGRAPHY.H6}
className="transaction-detail-item__title" className="transaction-detail-item__title"
@ -64,16 +65,9 @@ export default function TransactionDetailItem({
TransactionDetailItem.propTypes = { TransactionDetailItem.propTypes = {
detailTitle: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), detailTitle: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
detailTitleColor: PropTypes.string,
detailText: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), detailText: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
detailTotal: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), detailTotal: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
subTitle: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), subTitle: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
subText: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), subText: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
}; };
TransactionDetailItem.defaultProps = {
detailTitle: '',
detailText: '',
detailTotal: '',
subTitle: '',
subText: '',
};

@ -2,6 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import classnames from 'classnames'; import classnames from 'classnames';
import InfoTooltip from '../info-tooltip'; import InfoTooltip from '../info-tooltip';
import InfoTooltipIcon from '../info-tooltip/info-tooltip-icon';
const CLASSNAME_WARNING = 'actionable-message--warning'; const CLASSNAME_WARNING = 'actionable-message--warning';
const CLASSNAME_DANGER = 'actionable-message--danger'; const CLASSNAME_DANGER = 'actionable-message--danger';
@ -21,7 +22,7 @@ export default function ActionableMessage({
withRightButton = false, withRightButton = false,
type = false, type = false,
useIcon = false, useIcon = false,
iconFill = '#b8b8b8', iconFillColor = '',
}) { }) {
const actionableMessageClassName = classnames( const actionableMessageClassName = classnames(
'actionable-message', 'actionable-message',
@ -33,18 +34,7 @@ export default function ActionableMessage({
return ( return (
<div className={actionableMessageClassName}> <div className={actionableMessageClassName}>
{useIcon && ( {useIcon && <InfoTooltipIcon fillColor={iconFillColor} />}
<svg
viewBox="0 0 10 10"
xmlns="http://www.w3.org/2000/svg"
className="actionable-message__icon"
>
<path
d="M5 0C2.2 0 0 2.2 0 5s2.2 5 5 5 5-2.2 5-5-2.2-5-5-5zm0 2c.4 0 .7.3.7.7s-.3.7-.7.7-.7-.2-.7-.6.3-.8.7-.8zm.7 6H4.3V4.3h1.5V8z"
fill={iconFill}
/>
</svg>
)}
{infoTooltipText && ( {infoTooltipText && (
<InfoTooltip <InfoTooltip
position="left" position="left"
@ -98,5 +88,5 @@ ActionableMessage.propTypes = {
withRightButton: PropTypes.bool, withRightButton: PropTypes.bool,
infoTooltipText: PropTypes.string, infoTooltipText: PropTypes.string,
useIcon: PropTypes.bool, useIcon: PropTypes.bool,
iconFill: PropTypes.string, iconFillColor: PropTypes.string,
}; };

@ -78,7 +78,7 @@ export const withIcon = () => (
)} )}
className="actionable-message--left-aligned actionable-message--warning" className="actionable-message--left-aligned actionable-message--warning"
useIcon useIcon
iconFill="#f8c000" iconFillColor="#f8c000"
/> />
</div> </div>
); );

@ -1,5 +1,5 @@
.info-tooltip { .info-tooltip {
img { svg {
height: 12px; height: 12px;
width: 12px; width: 12px;
} }
@ -41,6 +41,18 @@
text-align: left; text-align: left;
color: $Grey-500; color: $Grey-500;
a {
color: $primary-1;
}
p {
margin-bottom: 12px;
&:last-child {
margin-bottom: 0;
}
}
} }
} }

@ -0,0 +1,17 @@
import React from 'react';
import PropTypes from 'prop-types';
export default function InfoTooltipIcon({ fillColor = '#b8b8b8' }) {
return (
<svg viewBox="0 0 10 10" xmlns="http://www.w3.org/2000/svg">
<path
d="M5 0C2.2 0 0 2.2 0 5s2.2 5 5 5 5-2.2 5-5-2.2-5-5-5zm0 2c.4 0 .7.3.7.7s-.3.7-.7.7-.7-.2-.7-.6.3-.8.7-.8zm.7 6H4.3V4.3h1.5V8z"
fill={fillColor}
/>
</svg>
);
}
InfoTooltipIcon.propTypes = {
fillColor: PropTypes.string,
};

@ -2,6 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import classnames from 'classnames'; import classnames from 'classnames';
import Tooltip from '../tooltip'; import Tooltip from '../tooltip';
import InfoTooltipIcon from './info-tooltip-icon';
const positionArrowClassMap = { const positionArrowClassMap = {
top: 'info-tooltip__top-tooltip-arrow', top: 'info-tooltip__top-tooltip-arrow',
@ -16,6 +17,7 @@ export default function InfoTooltip({
containerClassName, containerClassName,
wrapperClassName, wrapperClassName,
wide, wide,
iconFillColor = '',
}) { }) {
return ( return (
<div className="info-tooltip"> <div className="info-tooltip">
@ -32,7 +34,7 @@ export default function InfoTooltip({
html={contentText} html={contentText}
theme={wide ? 'tippy-tooltip-wideInfo' : 'tippy-tooltip-info'} theme={wide ? 'tippy-tooltip-wideInfo' : 'tippy-tooltip-info'}
> >
<img src="images/mm-info-icon.svg" alt="" /> <InfoTooltipIcon fillColor={iconFillColor} />
</Tooltip> </Tooltip>
</div> </div>
); );
@ -44,4 +46,5 @@ InfoTooltip.propTypes = {
wide: PropTypes.bool, wide: PropTypes.bool,
containerClassName: PropTypes.string, containerClassName: PropTypes.string,
wrapperClassName: PropTypes.string, wrapperClassName: PropTypes.string,
iconFillColor: PropTypes.string,
}; };

@ -35,6 +35,8 @@ import TransactionDetail from '../../components/app/transaction-detail/transacti
import TransactionDetailItem from '../../components/app/transaction-detail-item/transaction-detail-item.component'; import TransactionDetailItem from '../../components/app/transaction-detail-item/transaction-detail-item.component';
import InfoTooltip from '../../components/ui/info-tooltip/info-tooltip'; import InfoTooltip from '../../components/ui/info-tooltip/info-tooltip';
import { COLORS } from '../../helpers/constants/design-system';
export default class ConfirmTransactionBase extends Component { export default class ConfirmTransactionBase extends Component {
static contextTypes = { static contextTypes = {
t: PropTypes.func, t: PropTypes.func,
@ -292,6 +294,7 @@ export default class ConfirmTransactionBase extends Component {
isMainnet, isMainnet,
isEthGasPrice, isEthGasPrice,
noGasPrice, noGasPrice,
txData,
} = this.props; } = this.props;
const { t } = this.context; const { t } = this.context;
@ -363,13 +366,40 @@ export default class ConfirmTransactionBase extends Component {
<TransactionDetailItem <TransactionDetailItem
key="gas-item" key="gas-item"
detailTitle={ detailTitle={
<> txData.dappSuggestedGasFees ? (
{t('transactionDetailGasHeading')} <>
<InfoTooltip contentText="" position="top"> {t('transactionDetailDappGasHeading', [txData.origin])}
<i className="fa fa-info-circle" /> <InfoTooltip
</InfoTooltip> contentText={t('transactionDetailDappGasTooltip')}
</> position="top"
iconFillColor="#f66a0a"
>
<i className="fa fa-info-circle" />
</InfoTooltip>
</>
) : (
<>
{t('transactionDetailGasHeading')}
<InfoTooltip
contentText={
<>
<p>{t('transactionDetailGasTooltipIntro')}</p>
<p>{t('transactionDetailGasTooltipExplanation')}</p>
<p>
<a href="https://community.metamask.io/t/what-is-gas-why-do-transactions-take-so-long/3172">
{t('transactionDetailGasTooltipConversion')}
</a>
</p>
</>
}
position="top"
>
<i className="fa fa-info-circle" />
</InfoTooltip>
</>
)
} }
detailTitleColor={COLORS.SECONDARY1}
detailText={ detailText={
<UserPreferencedCurrencyDisplay <UserPreferencedCurrencyDisplay
type={PRIMARY} type={PRIMARY}

@ -59,10 +59,15 @@ exports[`FeeCard renders the component with initial props 2`] = `
style="display: inline;" style="display: inline;"
tabindex="0" tabindex="0"
> >
<img <svg
alt="" viewBox="0 0 10 10"
src="images/mm-info-icon.svg" xmlns="http://www.w3.org/2000/svg"
/> >
<path
d="M5 0C2.2 0 0 2.2 0 5s2.2 5 5 5 5-2.2 5-5-2.2-5-5-5zm0 2c.4 0 .7.3.7.7s-.3.7-.7.7-.7-.2-.7-.6.3-.8.7-.8zm.7 6H4.3V4.3h1.5V8z"
fill=""
/>
</svg>
</div> </div>
</div> </div>
</div> </div>

@ -27,10 +27,7 @@ exports[`SortList renders the component with initial props 1`] = `
style="display: inline;" style="display: inline;"
tabindex="0" tabindex="0"
> >
<img &lt;InfoTooltipIcon /&gt;
alt=""
src="images/mm-info-icon.svg"
/>
</div> </div>
</div> </div>
</div> </div>
@ -69,10 +66,7 @@ exports[`SortList renders the component with initial props 2`] = `
style="display: inline;" style="display: inline;"
tabindex="0" tabindex="0"
> >
<img &lt;InfoTooltipIcon /&gt;
alt=""
src="images/mm-info-icon.svg"
/>
</div> </div>
</div> </div>
</div> </div>

@ -3,6 +3,11 @@ import React from 'react';
import { renderWithProvider } from '../../../../../test/jest'; import { renderWithProvider } from '../../../../../test/jest';
import SortList from './sort-list'; import SortList from './sort-list';
jest.mock(
'../../../../components/ui/info-tooltip/info-tooltip-icon',
() => () => '<InfoTooltipIcon />',
);
const createProps = (customProps = {}) => { const createProps = (customProps = {}) => {
return { return {
selectedAggId: 'Agg2', selectedAggId: 'Agg2',

@ -126,10 +126,7 @@ exports[`ViewQuote renders the component with initial props 4`] = `
style="display: inline;" style="display: inline;"
tabindex="0" tabindex="0"
> >
<img &lt;InfoTooltipIcon /&gt;
alt=""
src="images/mm-info-icon.svg"
/>
</div> </div>
</div> </div>
</div> </div>

@ -9,6 +9,10 @@ import {
} from '../../../../test/jest'; } from '../../../../test/jest';
import ViewQuote from '.'; import ViewQuote from '.';
jest.mock('../../../components/ui/info-tooltip/info-tooltip-icon', () => () =>
'<InfoTooltipIcon />',
);
const middleware = [thunk]; const middleware = [thunk];
const createProps = (customProps = {}) => { const createProps = (customProps = {}) => {
return { return {

Loading…
Cancel
Save