diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json index 60c253edd..20b058e08 100644 --- a/app/_locales/en/messages.json +++ b/app/_locales/en/messages.json @@ -429,6 +429,9 @@ "basic": { "message": "Basic" }, + "beCareful": { + "message": "Be careful" + }, "betaMetamaskDescription": { "message": "Trusted by millions, MetaMask is a secure wallet making the world of web3 accessible to all." }, @@ -831,6 +834,9 @@ "customSpendLimit": { "message": "Custom spend limit" }, + "customSpendingCap": { + "message": "Custom spending cap" + }, "customToken": { "message": "Custom token" }, @@ -2841,6 +2847,9 @@ "message": "By revoking permission, the following $1 will no longer be able to access your $2", "description": "$1 is either key 'account' or 'contract', and $2 is either a string or link of a given token symbol or name" }, + "revokeSpendingCapTooltipText": { + "message": "This contract will be unable to spend any more of your current or future tokens." + }, "rinkeby": { "message": "Rinkeby test network" }, @@ -4199,6 +4208,10 @@ "warning": { "message": "Warning" }, + "warningTooltipText": { + "message": "$1 The contract could spend your entire token balance without further notice or consent. Protect yourself by customizing a lower spending cap.", + "description": "$1 is a fa-exclamation-circle icon with text 'Be careful' in 'warning' colour" + }, "weak": { "message": "Weak" }, diff --git a/ui/components/ui/review-spending-cap/index.js b/ui/components/ui/review-spending-cap/index.js new file mode 100644 index 000000000..f36dfc808 --- /dev/null +++ b/ui/components/ui/review-spending-cap/index.js @@ -0,0 +1 @@ +export { default } from './review-spending-cap'; diff --git a/ui/components/ui/review-spending-cap/index.scss b/ui/components/ui/review-spending-cap/index.scss new file mode 100644 index 000000000..110edfdf4 --- /dev/null +++ b/ui/components/ui/review-spending-cap/index.scss @@ -0,0 +1,33 @@ +.review-spending-cap { + &__heading { + width: 100%; + } + + &__heading-title { + &__tooltip { + width: 180px; + + &__warning-icon { + color: var(--color-warning-default); + } + + &__question-icon { + color: var(--color-icon-muted); + } + } + } + + &__heading-detail { + flex-grow: 1; + align-self: center; + + &__button { + background: none; + color: var(--color-primary-default); + } + } + + i { + font-size: $font-size-h7; + } +} diff --git a/ui/components/ui/review-spending-cap/review-spending-cap.js b/ui/components/ui/review-spending-cap/review-spending-cap.js new file mode 100644 index 000000000..760471628 --- /dev/null +++ b/ui/components/ui/review-spending-cap/review-spending-cap.js @@ -0,0 +1,131 @@ +import React, { useContext } from 'react'; +import PropTypes from 'prop-types'; +import { I18nContext } from '../../../contexts/i18n'; +import Box from '../box'; +import Tooltip from '../tooltip'; +import Typography from '../typography'; +import { + ALIGN_ITEMS, + COLORS, + DISPLAY, + FLEX_DIRECTION, + FONT_WEIGHT, + TYPOGRAPHY, + TEXT_ALIGN, + SIZES, +} from '../../../helpers/constants/design-system'; + +export default function ReviewSpendingCap({ + tokenName, + currentTokenBalance, + tokenValue, + onEdit, +}) { + const t = useContext(I18nContext); + + return ( + + + + + {t('customSpendingCap')} + + + + {tokenValue > currentTokenBalance && + t('warningTooltipText', [ + + {' '} + {t('beCareful')} + , + ])} + {tokenValue === 0 && t('revokeSpendingCapTooltipText')} + + } + > + {tokenValue > currentTokenBalance && ( + + )} + {tokenValue === 0 && ( + + )} + + + + + + + + + currentTokenBalance + ? COLORS.WARNING_DEFAULT + : COLORS.TEXT_DEFAULT + } + variant={TYPOGRAPHY.H6} + marginBottom={3} + > + {tokenValue} {tokenName} + + + + ); +} + +ReviewSpendingCap.propTypes = { + tokenName: PropTypes.string, + currentTokenBalance: PropTypes.number, + tokenValue: PropTypes.number, + onEdit: PropTypes.func, +}; diff --git a/ui/components/ui/review-spending-cap/review-spending-cap.stories.js b/ui/components/ui/review-spending-cap/review-spending-cap.stories.js new file mode 100644 index 000000000..83832a966 --- /dev/null +++ b/ui/components/ui/review-spending-cap/review-spending-cap.stories.js @@ -0,0 +1,32 @@ +import React from 'react'; +import ReviewSpendingCap from './review-spending-cap'; + +export default { + title: 'Components/UI/ReviewSpendingCap', + id: __filename, + argTypes: { + tokenName: { + control: { type: 'text' }, + }, + currentTokenBalance: { + control: { type: 'number' }, + }, + tokenValue: { + control: { type: 'number' }, + }, + onEdit: { + action: 'onEdit', + }, + }, + args: { + tokenName: 'DAI', + currentTokenBalance: 200.12, + tokenValue: 7, + }, +}; + +export const DefaultStory = (args) => { + return ; +}; + +DefaultStory.storyName = 'Default'; diff --git a/ui/components/ui/typography/typography.js b/ui/components/ui/typography/typography.js index c1bb709d8..e8dbaa473 100644 --- a/ui/components/ui/typography/typography.js +++ b/ui/components/ui/typography/typography.js @@ -24,6 +24,7 @@ export const ValidColors = [ COLORS.ERROR_INVERSE, COLORS.SUCCESS_DEFAULT, COLORS.SUCCESS_INVERSE, + COLORS.WARNING_DEFAULT, COLORS.WARNING_INVERSE, COLORS.INFO_DEFAULT, COLORS.INFO_INVERSE, diff --git a/ui/components/ui/ui-components.scss b/ui/components/ui/ui-components.scss index 24cb6ddec..b47976bea 100644 --- a/ui/components/ui/ui-components.scss +++ b/ui/components/ui/ui-components.scss @@ -41,6 +41,7 @@ @import 'pulse-loader/index'; @import 'qr-code/index'; @import 'radio-group/index'; +@import 'review-spending-cap/index'; @import 'sender-to-recipient/index'; @import 'show-hide-toggle/index.scss'; @import 'snackbar/index';