@stokr/components-library 3.0.53 → 3.0.55
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/Input/InputPassword.js +7 -11
- package/dist/components/RegisterModal/RegisterModal.js +12 -7
- package/dist/components/ResetPasswordModal/ResetPasswordModal.js +12 -13
- package/dist/index.js +11 -1
- package/dist/utils/formatCurrencyValue.js +35 -25
- package/dist/utils/password-validation.js +49 -0
- package/package.json +1 -1
|
@@ -4,12 +4,13 @@ import PropTypes from "prop-types";
|
|
|
4
4
|
import stdin_default$1 from "../SvgIcons/CapsLockSvg.js";
|
|
5
5
|
import { InfoIcon } from "../InfoIcon/InfoIcon.js";
|
|
6
6
|
import { Wrapper, Label, InputWrap } from "./Input.styles.js";
|
|
7
|
+
import { getPasswordEntropy, ENTROPY_WEAK, ENTROPY_MEDIUM } from "../../utils/password-validation.js";
|
|
7
8
|
import { InfoIconWrapper, ShowPassword, BottomWrap, PasswordStrengthWrap, PasswordStrengthIndicators, PasswordStrengthIndicator, PasswordStrengthText, CapslockIndicator } from "./InputPassword.styles.js";
|
|
8
9
|
const PasswordStrength = {
|
|
9
10
|
NONE: "none",
|
|
10
11
|
WEAK: "Weak",
|
|
11
|
-
MEDIUM: "
|
|
12
|
-
STRONG: "
|
|
12
|
+
MEDIUM: "Good",
|
|
13
|
+
STRONG: "Strong"
|
|
13
14
|
};
|
|
14
15
|
const InputPassword = (props) => {
|
|
15
16
|
const {
|
|
@@ -53,15 +54,10 @@ const InputPassword = (props) => {
|
|
|
53
54
|
onChange?.(e, field);
|
|
54
55
|
};
|
|
55
56
|
const passwordStrengthFunction = () => {
|
|
56
|
-
if (value
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
if (
|
|
60
|
-
return PasswordStrength.WEAK;
|
|
61
|
-
}
|
|
62
|
-
if (value.length < 11) {
|
|
63
|
-
return PasswordStrength.MEDIUM;
|
|
64
|
-
}
|
|
57
|
+
if (!value) return PasswordStrength.NONE;
|
|
58
|
+
const entropy = getPasswordEntropy(value);
|
|
59
|
+
if (entropy < ENTROPY_WEAK) return PasswordStrength.WEAK;
|
|
60
|
+
if (entropy < ENTROPY_MEDIUM) return PasswordStrength.MEDIUM;
|
|
65
61
|
return PasswordStrength.STRONG;
|
|
66
62
|
};
|
|
67
63
|
const passwordStrength = passwordStrengthFunction();
|
|
@@ -16,6 +16,7 @@ import { Row, Column } from "../Grid/Grid.styles.js";
|
|
|
16
16
|
import { ComponentWrapper } from "../ComponentWrapper/ComponentWrapper.styles.js";
|
|
17
17
|
import fetchDataPublic from "../../api/fetchDataPublic.js";
|
|
18
18
|
import { emailRegex } from "../../constants/globalVariables.js";
|
|
19
|
+
import { isPasswordValid, PASSWORD_REQUIREMENTS_MESSAGE } from "../../utils/password-validation.js";
|
|
19
20
|
import { ModalInner, ModalLinkWrap, ModalLink } from "../Modal/Modal.styles.js";
|
|
20
21
|
import { FormField, FormError } from "../Form/Form.styles.js";
|
|
21
22
|
const TermsStyled = styled.span`
|
|
@@ -78,7 +79,11 @@ const RegisterModal = (props) => {
|
|
|
78
79
|
}, [props.popupError]);
|
|
79
80
|
const validationSchema = Yup.object().shape({
|
|
80
81
|
email: Yup.string().matches(emailRegex, "Oops, that's not a valid address").required("Oops, this can‘t be blank"),
|
|
81
|
-
password: Yup.string().
|
|
82
|
+
password: Yup.string().test("password-full", function(val) {
|
|
83
|
+
if (!val) return this.createError({ message: "Oops, this can’t be blank" });
|
|
84
|
+
if (!isPasswordValid(val)) return this.createError({ message: PASSWORD_REQUIREMENTS_MESSAGE });
|
|
85
|
+
return true;
|
|
86
|
+
}),
|
|
82
87
|
terms: Yup.bool().oneOf([true], "Please agree to continue"),
|
|
83
88
|
newsletter: Yup.bool()
|
|
84
89
|
// .oneOf([true], 'Newsletter accept is required'),
|
|
@@ -136,13 +141,13 @@ const RegisterModal = (props) => {
|
|
|
136
141
|
clearPopupError();
|
|
137
142
|
}
|
|
138
143
|
if (withTouch) {
|
|
139
|
-
setFieldValue(field.name, field.value,
|
|
140
|
-
setFieldTouched(field.name);
|
|
144
|
+
setFieldValue(field.name, field.value, true);
|
|
145
|
+
setFieldTouched(field.name, true, false);
|
|
141
146
|
} else {
|
|
142
147
|
handleChange(e);
|
|
143
148
|
}
|
|
144
149
|
};
|
|
145
|
-
const submitDisabled = !values.email || !!errors.email || !!errors.terms || values.password
|
|
150
|
+
const submitDisabled = !values.email || !!errors.email || !!errors.terms || !isPasswordValid(values.password) || isActionLoading === "signUp" || popupError.popup === "register";
|
|
146
151
|
return /* @__PURE__ */ jsxs(stdin_default$1, { children: [
|
|
147
152
|
/* @__PURE__ */ jsx(ComponentWrapper, { noPadding: true, children: /* @__PURE__ */ jsxs(FormField, { children: [
|
|
148
153
|
/* @__PURE__ */ jsx(
|
|
@@ -179,11 +184,11 @@ const RegisterModal = (props) => {
|
|
|
179
184
|
touched: !!touched.password,
|
|
180
185
|
info: "For a stronger password, try a mix of lowercase, capitals, numbers and special characters.",
|
|
181
186
|
autoComplete: "new-password",
|
|
182
|
-
"data-cy": "register-modal-password-input"
|
|
187
|
+
"data-cy": "register-modal-password-input",
|
|
188
|
+
showStrength: true
|
|
183
189
|
}
|
|
184
190
|
),
|
|
185
|
-
/* @__PURE__ */ jsx(FormError, { show: errors.password && touched.password, children: errors.password })
|
|
186
|
-
/* @__PURE__ */ jsx(FormError, { show: !errors.password && touched.password && values.password.length <= 5, children: "The password must be at least 6 characters long" })
|
|
191
|
+
/* @__PURE__ */ jsx(FormError, { show: errors.password && touched.password, children: errors.password })
|
|
187
192
|
] }) }),
|
|
188
193
|
/* @__PURE__ */ jsx(ComponentWrapper, { noPaddingBottom: true, noPaddingHorizontal: true }),
|
|
189
194
|
/* @__PURE__ */ jsxs(ComponentWrapper, { noPaddingBottom: true, noPaddingHorizontal: true, children: [
|
|
@@ -10,6 +10,7 @@ import stdin_default$2 from "../Input/InputPassword.js";
|
|
|
10
10
|
import { Button } from "../Button/Button.styles.js";
|
|
11
11
|
import { Row, Column } from "../Grid/Grid.styles.js";
|
|
12
12
|
import { ComponentWrapper } from "../ComponentWrapper/ComponentWrapper.styles.js";
|
|
13
|
+
import { isPasswordValid, PASSWORD_REQUIREMENTS_MESSAGE } from "../../utils/password-validation.js";
|
|
13
14
|
import { ModalInner, ModalLinkWrap, ModalLink } from "../Modal/Modal.styles.js";
|
|
14
15
|
import { FormField, FormError } from "../Form/Form.styles.js";
|
|
15
16
|
const ResetPasswordModal = ({
|
|
@@ -26,7 +27,11 @@ const ResetPasswordModal = ({
|
|
|
26
27
|
confirmPassword: ""
|
|
27
28
|
};
|
|
28
29
|
const validationSchema = Yup.object().shape({
|
|
29
|
-
password: Yup.string().
|
|
30
|
+
password: Yup.string().test("password-full", function(val) {
|
|
31
|
+
if (!val) return this.createError({ message: "Oops, this can’t be blank" });
|
|
32
|
+
if (!isPasswordValid(val)) return this.createError({ message: PASSWORD_REQUIREMENTS_MESSAGE });
|
|
33
|
+
return true;
|
|
34
|
+
}),
|
|
30
35
|
confirmPassword: Yup.string().required("Oops, this can‘t be blank")
|
|
31
36
|
});
|
|
32
37
|
useEffect(() => {
|
|
@@ -54,10 +59,10 @@ const ResetPasswordModal = ({
|
|
|
54
59
|
children: ({ values, errors, touched, handleBlur, setFieldValue, setFieldTouched }) => {
|
|
55
60
|
const onChangeWithTouch = (e) => {
|
|
56
61
|
const field = e.target;
|
|
57
|
-
setFieldValue(field.name, field.value,
|
|
58
|
-
setFieldTouched(field.name);
|
|
62
|
+
setFieldValue(field.name, field.value, true);
|
|
63
|
+
setFieldTouched(field.name, true, false);
|
|
59
64
|
};
|
|
60
|
-
const submitDisabled = values.password
|
|
65
|
+
const submitDisabled = !isPasswordValid(values.password) || values.password !== values.confirmPassword || isActionLoading === "resetPassword";
|
|
61
66
|
return /* @__PURE__ */ jsxs(stdin_default$1, { children: [
|
|
62
67
|
/* @__PURE__ */ jsx("br", {}),
|
|
63
68
|
/* @__PURE__ */ jsx("br", {}),
|
|
@@ -76,17 +81,11 @@ const ResetPasswordModal = ({
|
|
|
76
81
|
touched: !!touched.password,
|
|
77
82
|
info: "For a stronger password, try a mix of lowercase, capitals, numbers and special characters.",
|
|
78
83
|
autoComplete: "new-password",
|
|
79
|
-
"data-cy": "reset-password-modal-password-input"
|
|
84
|
+
"data-cy": "reset-password-modal-password-input",
|
|
85
|
+
showStrength: true
|
|
80
86
|
}
|
|
81
87
|
),
|
|
82
|
-
/* @__PURE__ */ jsx(FormError, { show: errors.password && touched.password, children: errors.password })
|
|
83
|
-
/* @__PURE__ */ jsx(
|
|
84
|
-
FormError,
|
|
85
|
-
{
|
|
86
|
-
show: !errors.password && touched.password && values.password.length <= 5,
|
|
87
|
-
children: "The password must be at least 6 characters long"
|
|
88
|
-
}
|
|
89
|
-
)
|
|
88
|
+
/* @__PURE__ */ jsx(FormError, { show: errors.password && touched.password, children: errors.password })
|
|
90
89
|
] }) }),
|
|
91
90
|
/* @__PURE__ */ jsx("br", {}),
|
|
92
91
|
/* @__PURE__ */ jsx(ComponentWrapper, { noPadding: true, children: /* @__PURE__ */ jsxs(FormField, { children: [
|
package/dist/index.js
CHANGED
|
@@ -130,8 +130,9 @@ import { generateCoreChecklistTasks, getVerifyIdentityChecklist } from "./utils/
|
|
|
130
130
|
import { CopyToClipBoardTooltip, copyTextToClipboard, useCopyToClipboard, withCopyToClipboard } from "./utils/copyToClipboard.js";
|
|
131
131
|
import { cooldownHOC, useComponentVisible, useContainerSize, useCooldown, useMobileView, usePrevious } from "./utils/customHooks.js";
|
|
132
132
|
import { fixDecimals } from "./utils/fix-decimals.js";
|
|
133
|
-
import { formatCurrencyValue, getCurrencyIcon, getCurrencySymbol, getLiquidAssetIcon, getProjectCurrencySign } from "./utils/formatCurrencyValue.js";
|
|
133
|
+
import { CURRENCY_CONFIG, formatCurrencyValue, getCurrencyConfig, getCurrencyIcon, getCurrencySymbol, getLiquidAssetIcon, getProjectCurrencySign } from "./utils/formatCurrencyValue.js";
|
|
134
134
|
import { isUSInvestor, usCountries } from "./utils/isUSInvestor.js";
|
|
135
|
+
import { ENTROPY_MEDIUM, ENTROPY_WEAK, PASSWORD_MIN_LENGTH, PASSWORD_REQUIREMENTS_MESSAGE, getPasswordCriteria, getPasswordEntropy, isPasswordValid } from "./utils/password-validation.js";
|
|
135
136
|
import { km_ify } from "./utils/km_ify.js";
|
|
136
137
|
import { momentUtils } from "./utils/moment.js";
|
|
137
138
|
import { openFile, saveAs } from "./utils/saveAs.js";
|
|
@@ -276,6 +277,7 @@ export {
|
|
|
276
277
|
ButtonGridContainer,
|
|
277
278
|
CAPITAL_ANIMATION_LENGTH,
|
|
278
279
|
COLUMN,
|
|
280
|
+
CURRENCY_CONFIG,
|
|
279
281
|
default10 as CameraSvg,
|
|
280
282
|
default11 as CapsLockSvg,
|
|
281
283
|
CaptialRaisedSummary,
|
|
@@ -330,6 +332,8 @@ export {
|
|
|
330
332
|
default14 as DocumentSmallSvg,
|
|
331
333
|
default15 as DocumentSvg,
|
|
332
334
|
DoubleButtons,
|
|
335
|
+
ENTROPY_MEDIUM,
|
|
336
|
+
ENTROPY_WEAK,
|
|
333
337
|
default16 as Enable2FAFlow,
|
|
334
338
|
default17 as EnterCode,
|
|
335
339
|
ErrorMessage,
|
|
@@ -447,6 +451,8 @@ export {
|
|
|
447
451
|
OptionLabel,
|
|
448
452
|
OtpInput,
|
|
449
453
|
Outer,
|
|
454
|
+
PASSWORD_MIN_LENGTH,
|
|
455
|
+
PASSWORD_REQUIREMENTS_MESSAGE,
|
|
450
456
|
PHONE_MEDIA,
|
|
451
457
|
PageTransition,
|
|
452
458
|
PageWrapper,
|
|
@@ -613,11 +619,14 @@ export {
|
|
|
613
619
|
getAnalyticsIngestUrl,
|
|
614
620
|
getBackofficeAppUrl,
|
|
615
621
|
getConfig,
|
|
622
|
+
getCurrencyConfig,
|
|
616
623
|
getCurrencyIcon,
|
|
617
624
|
getCurrencySymbol,
|
|
618
625
|
getFooterGroups,
|
|
619
626
|
getLiquidAssetIcon,
|
|
620
627
|
getMedia,
|
|
628
|
+
getPasswordCriteria,
|
|
629
|
+
getPasswordEntropy,
|
|
621
630
|
getProjectCurrencySign,
|
|
622
631
|
getTokenBucket,
|
|
623
632
|
getVerifyIdentityChecklist,
|
|
@@ -629,6 +638,7 @@ export {
|
|
|
629
638
|
isAccountLockedError,
|
|
630
639
|
isAlreadyOnOnboardingFlow,
|
|
631
640
|
isExternalUrl,
|
|
641
|
+
isPasswordValid,
|
|
632
642
|
isUSInvestor,
|
|
633
643
|
km_ify,
|
|
634
644
|
learnMoreCategoryPropTypes,
|
|
@@ -4,32 +4,40 @@ import BitcoinLogo from "../static/images/currency/bitcoin-logo.png.js";
|
|
|
4
4
|
import BMN2Logo from "../static/images/currency/bmn2-logo.png.js";
|
|
5
5
|
import ETHLogo from "../static/images/currency/eth_logo.svg.js";
|
|
6
6
|
import USDTLogo from "../static/images/currency/usdt-logo.png.js";
|
|
7
|
+
const CURRENCY_CONFIG = {
|
|
8
|
+
tether: ["USDT", 2],
|
|
9
|
+
gemini: ["GUSD", 2],
|
|
10
|
+
ether: ["ETH", 4],
|
|
11
|
+
artid: ["ARTID", 2],
|
|
12
|
+
bitcoin: ["BTC", 8],
|
|
13
|
+
lbtc: ["LBTC", 8],
|
|
14
|
+
"lusdt-bfx": ["USDT", 2],
|
|
15
|
+
"lbtc-bfx": ["LBTC", 8],
|
|
16
|
+
"btc-bfx": ["BTC", 8],
|
|
17
|
+
euro: ["EUR", 2],
|
|
18
|
+
usd: ["USD", 2],
|
|
19
|
+
chf: ["CHF", 2],
|
|
20
|
+
lusdt: ["USDT", 2],
|
|
21
|
+
"bitcoin-private": ["EUR", 2],
|
|
22
|
+
"lbtc-private": ["BTC", 8],
|
|
23
|
+
bmn1: ["BMN1", 2],
|
|
24
|
+
bmn2: ["BMN2", 4],
|
|
25
|
+
"btc-fb": ["BTC", 8],
|
|
26
|
+
"usdc-fb": ["USDC", 2],
|
|
27
|
+
"usdq-fb": ["USDQ", 2],
|
|
28
|
+
xaut: ["XAUt", 6],
|
|
29
|
+
"usd-in-kind": ["USD", 2]
|
|
30
|
+
};
|
|
31
|
+
const getCurrencyConfig = (currency, tokenDecimals = 2) => {
|
|
32
|
+
const [symbol, decimals] = CURRENCY_CONFIG[currency] || [currency, tokenDecimals];
|
|
33
|
+
return { symbol, decimals };
|
|
34
|
+
};
|
|
7
35
|
const formatCurrencyValue = (currency, value, tokenDecimals = 2, options = {}) => {
|
|
8
|
-
const { valueFirst = false } = options;
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
artid: ["ARTID", 2],
|
|
14
|
-
bitcoin: ["BTC", 8],
|
|
15
|
-
lbtc: ["LBTC", 8],
|
|
16
|
-
"lusdt-bfx": ["USDT", 2],
|
|
17
|
-
"lbtc-bfx": ["LBTC", 8],
|
|
18
|
-
"btc-bfx": ["BTC", 8],
|
|
19
|
-
euro: ["EUR", 2],
|
|
20
|
-
usd: ["USD", 2],
|
|
21
|
-
chf: ["CHF", 2],
|
|
22
|
-
lusdt: ["USDT", 2],
|
|
23
|
-
"bitcoin-private": ["EUR", 2],
|
|
24
|
-
"lbtc-private": ["BTC", 8],
|
|
25
|
-
bmn1: ["BMN1", 2],
|
|
26
|
-
bmn2: ["BMN2", 4],
|
|
27
|
-
"btc-fb": ["BTC", 8],
|
|
28
|
-
"usdc-fb": ["USDC", 2],
|
|
29
|
-
"usdq-fb": ["USDQ", 2],
|
|
30
|
-
xaut: ["XAUt", 6],
|
|
31
|
-
"usd-in-kind": ["USD", 2]
|
|
32
|
-
}[currency] || [currency, tokenDecimals];
|
|
36
|
+
const { valueFirst = false, formatter } = options;
|
|
37
|
+
const { symbol, decimals } = getCurrencyConfig(currency, tokenDecimals);
|
|
38
|
+
if (typeof formatter === "function") {
|
|
39
|
+
return formatter({ symbol, decimals, value, valueFirst, currency });
|
|
40
|
+
}
|
|
33
41
|
const formattedValue = parseFloat(Math.abs(value)).toFixed(decimals);
|
|
34
42
|
return valueFirst ? `${formattedValue} ${symbol}` : `${symbol} ${formattedValue}`;
|
|
35
43
|
};
|
|
@@ -93,7 +101,9 @@ const getLiquidAssetIcon = async (assetId) => {
|
|
|
93
101
|
});
|
|
94
102
|
};
|
|
95
103
|
export {
|
|
104
|
+
CURRENCY_CONFIG,
|
|
96
105
|
formatCurrencyValue,
|
|
106
|
+
getCurrencyConfig,
|
|
97
107
|
getCurrencyIcon,
|
|
98
108
|
getCurrencySymbol,
|
|
99
109
|
getLiquidAssetIcon,
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
const PASSWORD_MIN_LENGTH = 8;
|
|
2
|
+
const PASSWORD_REQUIREMENTS_MESSAGE = "Use at least 8 characters with a mix of letters, numbers, and symbols.";
|
|
3
|
+
const ENTROPY_WEAK = 50;
|
|
4
|
+
const ENTROPY_MEDIUM = 80;
|
|
5
|
+
const getCharsetSize = (password) => {
|
|
6
|
+
let size = 0;
|
|
7
|
+
if (/[a-z]/.test(password)) size += 26;
|
|
8
|
+
if (/[A-Z]/.test(password)) size += 26;
|
|
9
|
+
if (/[0-9]/.test(password)) size += 10;
|
|
10
|
+
if (/[^A-Za-z0-9]/.test(password)) size += 32;
|
|
11
|
+
return size;
|
|
12
|
+
};
|
|
13
|
+
const getPasswordEntropy = (value) => {
|
|
14
|
+
const password = value || "";
|
|
15
|
+
if (password.length === 0) return 0;
|
|
16
|
+
const charsetSize = getCharsetSize(password);
|
|
17
|
+
const uniqueChars = new Set(password).size;
|
|
18
|
+
const effectiveLength = (password.length + uniqueChars) / 2;
|
|
19
|
+
return effectiveLength * Math.log2(charsetSize);
|
|
20
|
+
};
|
|
21
|
+
const getPasswordCriteria = (value) => {
|
|
22
|
+
const password = value || "";
|
|
23
|
+
const hasLowercase = /[a-z]/.test(password);
|
|
24
|
+
const hasUppercase = /[A-Z]/.test(password);
|
|
25
|
+
const hasNumber = /[0-9]/.test(password);
|
|
26
|
+
const hasSpecial = /[^A-Za-z0-9]/.test(password);
|
|
27
|
+
return {
|
|
28
|
+
length: password.length,
|
|
29
|
+
hasLength: password.length >= PASSWORD_MIN_LENGTH,
|
|
30
|
+
hasLowercase,
|
|
31
|
+
hasUppercase,
|
|
32
|
+
hasNumber,
|
|
33
|
+
hasSpecial,
|
|
34
|
+
classesMet: [hasLowercase, hasUppercase, hasNumber, hasSpecial].filter(Boolean).length
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
const isPasswordValid = (value) => {
|
|
38
|
+
const password = value || "";
|
|
39
|
+
return password.length >= PASSWORD_MIN_LENGTH && getPasswordEntropy(password) >= ENTROPY_WEAK;
|
|
40
|
+
};
|
|
41
|
+
export {
|
|
42
|
+
ENTROPY_MEDIUM,
|
|
43
|
+
ENTROPY_WEAK,
|
|
44
|
+
PASSWORD_MIN_LENGTH,
|
|
45
|
+
PASSWORD_REQUIREMENTS_MESSAGE,
|
|
46
|
+
getPasswordCriteria,
|
|
47
|
+
getPasswordEntropy,
|
|
48
|
+
isPasswordValid
|
|
49
|
+
};
|