@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.
@@ -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: "Average",
12
- STRONG: "Excellent"
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.length === 0) {
57
- return PasswordStrength.NONE;
58
- }
59
- if (value.length < 6) {
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().required("Oops, this can‘t be blank"),
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, false);
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.length <= 5 || isActionLoading === "signUp" || popupError.popup === "register";
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().required("Oops, this can‘t be blank"),
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, false);
58
- setFieldTouched(field.name);
62
+ setFieldValue(field.name, field.value, true);
63
+ setFieldTouched(field.name, true, false);
59
64
  };
60
- const submitDisabled = values.password.length <= 5 || values.password !== values.confirmPassword || isActionLoading === "resetPassword";
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 [symbol, decimals] = {
10
- tether: ["USDT", 2],
11
- gemini: ["GUSD", 2],
12
- ether: ["ETH", 4],
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
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stokr/components-library",
3
- "version": "3.0.53",
3
+ "version": "3.0.55",
4
4
  "description": "STOKR - Components Library",
5
5
  "author": "Bilal Hodzic <bilal@stokr.io>",
6
6
  "license": "MIT",