@stokr/components-library 3.0.54 → 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
@@ -132,6 +132,7 @@ import { cooldownHOC, useComponentVisible, useContainerSize, useCooldown, useMob
132
132
  import { fixDecimals } from "./utils/fix-decimals.js";
133
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";
@@ -331,6 +332,8 @@ export {
331
332
  default14 as DocumentSmallSvg,
332
333
  default15 as DocumentSvg,
333
334
  DoubleButtons,
335
+ ENTROPY_MEDIUM,
336
+ ENTROPY_WEAK,
334
337
  default16 as Enable2FAFlow,
335
338
  default17 as EnterCode,
336
339
  ErrorMessage,
@@ -448,6 +451,8 @@ export {
448
451
  OptionLabel,
449
452
  OtpInput,
450
453
  Outer,
454
+ PASSWORD_MIN_LENGTH,
455
+ PASSWORD_REQUIREMENTS_MESSAGE,
451
456
  PHONE_MEDIA,
452
457
  PageTransition,
453
458
  PageWrapper,
@@ -620,6 +625,8 @@ export {
620
625
  getFooterGroups,
621
626
  getLiquidAssetIcon,
622
627
  getMedia,
628
+ getPasswordCriteria,
629
+ getPasswordEntropy,
623
630
  getProjectCurrencySign,
624
631
  getTokenBucket,
625
632
  getVerifyIdentityChecklist,
@@ -631,6 +638,7 @@ export {
631
638
  isAccountLockedError,
632
639
  isAlreadyOnOnboardingFlow,
633
640
  isExternalUrl,
641
+ isPasswordValid,
634
642
  isUSInvestor,
635
643
  km_ify,
636
644
  learnMoreCategoryPropTypes,
@@ -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.54",
3
+ "version": "3.0.55",
4
4
  "description": "STOKR - Components Library",
5
5
  "author": "Bilal Hodzic <bilal@stokr.io>",
6
6
  "license": "MIT",