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';