@thecb/components 2.2.1 → 3.1.1

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.
Files changed (90) hide show
  1. package/.github/workflows/bump-version.yml +30 -0
  2. package/.github/workflows/create-release/build-body.sh +35 -0
  3. package/.github/workflows/create-release.yml +52 -0
  4. package/.github/workflows/disabled-workflows/publish-update.yml +73 -0
  5. package/README.md +68 -90
  6. package/dist/index.cjs.js +31180 -3325
  7. package/package.json +15 -35
  8. package/rollup.config.js +3 -1
  9. package/src/components/atoms/breadcrumb/Breadcrumb.js +0 -2
  10. package/src/components/atoms/button-with-action/ButtonWithAction.js +30 -4
  11. package/src/components/atoms/button-with-action/ButtonWithAction.theme.js +64 -234
  12. package/src/components/atoms/formatted-credit-card/FormattedCreditCard.js +53 -0
  13. package/src/components/atoms/formatted-credit-card/FormattedCreditCard.theme.js +9 -0
  14. package/src/components/atoms/formatted-credit-card/index.js +3 -0
  15. package/src/components/atoms/icons/AccountNumberImage.js +95 -0
  16. package/src/components/atoms/icons/BankIcon.js +82 -0
  17. package/src/components/atoms/icons/CheckmarkIcon.js +55 -0
  18. package/src/components/atoms/icons/GenericCard.js +39 -0
  19. package/src/components/atoms/icons/PaymentIcon.js +50 -0
  20. package/src/components/atoms/icons/RoutingNumberImage.js +95 -0
  21. package/src/components/atoms/icons/index.js +14 -1
  22. package/src/components/atoms/index.js +3 -0
  23. package/src/components/atoms/jumbo/Jumbo.js +76 -0
  24. package/src/components/atoms/jumbo/index.js +3 -0
  25. package/src/components/atoms/layouts/Box.js +0 -2
  26. package/src/components/atoms/layouts/Box.styled.js +1 -17
  27. package/src/components/atoms/layouts/Motion.styled.js +2 -5
  28. package/src/components/atoms/link/ExternalLink.js +3 -3
  29. package/src/components/atoms/link/ExternalLink.styled.js +9 -2
  30. package/src/components/atoms/link/InternalLink.js +2 -4
  31. package/src/components/atoms/link/InternalLink.styled.js +13 -15
  32. package/src/components/atoms/link/Link.theme.js +7 -1
  33. package/src/components/atoms/loading/Loading.js +17 -0
  34. package/src/components/atoms/loading/index.js +3 -0
  35. package/src/components/atoms/nav-header/NavHeader.js +1 -1
  36. package/src/components/atoms/placeholder/Placeholder.js +2 -1
  37. package/src/components/atoms/text/Text.js +0 -2
  38. package/src/components/atoms/text/Text.styled.js +2 -8
  39. package/src/components/atoms/toggle-switch/ToggleSwitch.js +1 -1
  40. package/src/components/index.js +1 -0
  41. package/src/components/molecules/account-and-routing-modal/AccountAndRoutingModal.js +74 -0
  42. package/src/components/molecules/account-and-routing-modal/AccountAndRoutingModal.theme.js +24 -0
  43. package/src/components/molecules/account-and-routing-modal/index.js +3 -0
  44. package/src/components/molecules/address-form/AddressForm.js +2 -1
  45. package/src/components/molecules/address-form/index.js +6 -6
  46. package/src/components/molecules/change-password-form/ChangePasswordForm.js +2 -1
  47. package/src/components/molecules/change-password-form/index.js +1 -1
  48. package/src/components/molecules/collapsible-section/CollapsibleSection.js +1 -1
  49. package/src/components/molecules/edit-name-form/EditNameForm.js +2 -1
  50. package/src/components/molecules/edit-name-form/index.js +1 -1
  51. package/src/components/molecules/editable-list/EditableList.js +139 -0
  52. package/src/components/molecules/editable-list/EditableList.styled.js +31 -0
  53. package/src/components/molecules/editable-list/index.js +3 -0
  54. package/src/components/molecules/editable-table/EditableTable.js +30 -0
  55. package/src/components/molecules/editable-table/EditableTable.styled.js +80 -0
  56. package/src/components/molecules/editable-table/TableListItem.js +64 -0
  57. package/src/components/molecules/editable-table/index.js +4 -0
  58. package/src/components/molecules/email-form/EmailForm.js +2 -1
  59. package/src/components/molecules/email-form/index.js +1 -1
  60. package/src/components/molecules/forgot-password-form/ForgotPasswordForm.js +2 -1
  61. package/src/components/molecules/forgot-password-form/index.js +1 -1
  62. package/src/components/molecules/index.js +5 -0
  63. package/src/components/molecules/login-form/LoginForm.js +2 -1
  64. package/src/components/molecules/login-form/index.js +1 -1
  65. package/src/components/molecules/module/Module.js +1 -3
  66. package/src/components/molecules/partial-amount-form/PartialAmountForm.js +73 -0
  67. package/src/components/molecules/partial-amount-form/PartialAmountForm.state.js +51 -0
  68. package/src/components/molecules/partial-amount-form/index.js +4 -0
  69. package/src/components/molecules/payment-form-ach/PaymentFormACH.js +189 -0
  70. package/src/components/molecules/payment-form-ach/PaymentFormACH.state.js +38 -0
  71. package/src/components/molecules/payment-form-ach/index.js +11 -0
  72. package/src/components/molecules/payment-form-card/PaymentFormCard.js +132 -0
  73. package/src/components/molecules/payment-form-card/PaymentFormCard.state.js +39 -0
  74. package/src/components/molecules/payment-form-card/index.js +11 -0
  75. package/src/components/molecules/phone-form/PhoneForm.js +2 -1
  76. package/src/components/molecules/phone-form/index.js +1 -1
  77. package/src/components/molecules/radio-section/RadioSection.js +1 -1
  78. package/src/components/molecules/registration-form/RegistrationForm.js +2 -1
  79. package/src/components/molecules/registration-form/index.js +1 -1
  80. package/src/components/molecules/reset-password-form/ResetPasswordForm.js +3 -1
  81. package/src/components/molecules/reset-password-form/index.js +1 -1
  82. package/src/components/molecules/tab-sidebar/TabSidebar.js +10 -5
  83. package/src/components/molecules/terms-and-conditions-modal/TermsAndConditionsModal.js +0 -1
  84. package/src/constants/index.js +4 -0
  85. package/src/index.js +3 -1
  86. package/src/util/formats.js +54 -2
  87. package/src/util/general.js +27 -4
  88. package/src/util/index.js +4 -0
  89. package/src/util/inputValidationUtils.js +0 -167
  90. package/src/util/router-utils.js +0 -23
@@ -0,0 +1,38 @@
1
+ import {
2
+ createFormState,
3
+ required,
4
+ matchesField,
5
+ onlyIntegers,
6
+ hasLength,
7
+ isRoutingNumber
8
+ } from "redux-freeform";
9
+
10
+ const formConfig = {
11
+ name: {
12
+ validators: [required()]
13
+ },
14
+ routingNumber: {
15
+ validators: [required(), hasLength(9, 9), isRoutingNumber()],
16
+ constraints: [onlyIntegers(), hasLength(0, 9)]
17
+ },
18
+ confirmRoutingNumber: {
19
+ validators: [matchesField("routingNumber")],
20
+ constraints: [onlyIntegers(), hasLength(0, 9)]
21
+ },
22
+ accountNumber: {
23
+ validators: [required(), hasLength(6, 17)],
24
+ constraints: [onlyIntegers(), hasLength(0, 17)]
25
+ },
26
+ confirmAccountNumber: {
27
+ validators: [matchesField("accountNumber")],
28
+ constraints: [onlyIntegers(), hasLength(0, 17)]
29
+ },
30
+ accountType: {
31
+ defaultValue: "CHECKING",
32
+ validators: [required()]
33
+ }
34
+ };
35
+
36
+ export const { reducer, mapStateToProps, mapDispatchToProps } = createFormState(
37
+ formConfig
38
+ );
@@ -0,0 +1,11 @@
1
+ import PaymentFormACH from "./PaymentFormACH";
2
+ import {
3
+ reducer,
4
+ mapStateToProps,
5
+ mapDispatchToProps
6
+ } from "./PaymentFormACH.state";
7
+
8
+ PaymentFormACH.reducer = reducer;
9
+ PaymentFormACH.mapStateToProps = mapStateToProps;
10
+ PaymentFormACH.mapDispatchToProps = mapDispatchToProps;
11
+ export default PaymentFormACH;
@@ -0,0 +1,132 @@
1
+ import React, { useEffect } from "react";
2
+ import { required, hasLength, matchesRegex } from "redux-freeform";
3
+ import { ifElse, isNil, always } from "ramda";
4
+ import { checkCardBrand, displayCurrency, noop } from "../../../util/general";
5
+ import { expirationDateFormat, creditCardFormat } from "../../../util/formats";
6
+ import {
7
+ FormInput,
8
+ FormInputColumn,
9
+ FormContainer,
10
+ FormInputRow
11
+ } from "../../atoms/form-layouts";
12
+ import { Box } from "../../atoms/layouts";
13
+ import Alert from "../../atoms/alert";
14
+
15
+ const nameOnCardErrors = {
16
+ [required.error]: "Name is required"
17
+ };
18
+ const creditCardNumberErrors = {
19
+ [required.error]: "Credit card number is required",
20
+ [hasLength.error]: "Credit card number is invalid"
21
+ };
22
+ const expirationDateErrors = {
23
+ [required.error]: "Expiration date is required",
24
+ [hasLength.error]: "Expiration date is invalid",
25
+ [matchesRegex.error]: "Expiration date is invalid"
26
+ };
27
+ const cvvErrors = {
28
+ [required.error]: "CVV is required",
29
+ [hasLength.error]: "CVV is invalid"
30
+ };
31
+ const zipCodeErrors = {
32
+ [required.error]: "Zip code is required",
33
+ [hasLength.error]: "Zip code is invalid"
34
+ };
35
+
36
+ const PaymentFormCard = ({
37
+ variant = "default",
38
+ hideZipCode = false,
39
+ clearOnDismount,
40
+ fields,
41
+ actions,
42
+ showErrors,
43
+ fees,
44
+ handleSubmit = noop,
45
+ isMobile
46
+ }) => {
47
+ useEffect(() => {
48
+ if (clearOnDismount) {
49
+ return () => actions.form.clear();
50
+ }
51
+ }, []);
52
+ return (
53
+ <FormContainer variant={variant} role="form" aria-label="Card payment">
54
+ <FormInputColumn>
55
+ <FormInput
56
+ labelTextWhenNoError="Name on card"
57
+ errorMessages={nameOnCardErrors}
58
+ field={fields.nameOnCard}
59
+ fieldActions={actions.fields.nameOnCard}
60
+ showErrors={showErrors}
61
+ onKeyDown={e => e.key === "Enter" && handleSubmit(e)}
62
+ />
63
+ <FormInput
64
+ labelTextWhenNoError="Credit card number"
65
+ errorMessages={creditCardNumberErrors}
66
+ field={fields.creditCardNumber}
67
+ fieldActions={actions.fields.creditCardNumber}
68
+ showErrors={showErrors}
69
+ formatter={creditCardFormat}
70
+ onKeyDown={e => e.key === "Enter" && handleSubmit(e)}
71
+ />
72
+ <FormInputRow breakpoint="20rem">
73
+ <FormInput
74
+ labelTextWhenNoError="Expiration date (MM/YY)"
75
+ errorMessages={expirationDateErrors}
76
+ field={fields.expirationDate}
77
+ fieldActions={actions.fields.expirationDate}
78
+ showErrors={showErrors}
79
+ formatter={expirationDateFormat}
80
+ onKeyDown={e => e.key === "Enter" && handleSubmit(e)}
81
+ />
82
+ <FormInput
83
+ labelTextWhenNoError="CVV"
84
+ errorMessages={cvvErrors}
85
+ field={fields.cvv}
86
+ fieldActions={actions.fields.cvv}
87
+ showErrors={showErrors}
88
+ background={
89
+ checkCardBrand(fields.creditCardNumber.rawValue) == "AMEX"
90
+ ? "/AmexCVVHint.svg"
91
+ : "/CVVHint.svg"
92
+ }
93
+ onKeyDown={e => e.key === "Enter" && handleSubmit(e)}
94
+ />
95
+ </FormInputRow>
96
+ {!hideZipCode && (
97
+ <Box
98
+ padding={isMobile ? "0" : "0 0.5rem 0 0"}
99
+ width={isMobile ? "100%" : "50%"}
100
+ >
101
+ <FormInput
102
+ labelTextWhenNoError="Zip code"
103
+ errorMessages={zipCodeErrors}
104
+ field={fields.zipCode}
105
+ fieldActions={actions.fields.zipCode}
106
+ showErrors={showErrors}
107
+ onKeyDown={e => e.key === "Enter" && handleSubmit(e)}
108
+ />
109
+ </Box>
110
+ )}
111
+ {!!fees?.value && (
112
+ <Alert
113
+ heading="Processing Fee"
114
+ text={`There is a processing fee of ${
115
+ fees.type === "FLAT"
116
+ ? `${displayCurrency(fees.value)}`
117
+ : `${fees.value * 100}%`
118
+ } ${ifElse(
119
+ isNil,
120
+ always(""),
121
+ a => `with a minimum of ${displayCurrency(a)} `
122
+ )(fees.minimumInCents)} on all card payments.`}
123
+ variant="info"
124
+ showQuitLink={false}
125
+ />
126
+ )}
127
+ </FormInputColumn>
128
+ </FormContainer>
129
+ );
130
+ };
131
+
132
+ export default PaymentFormCard;
@@ -0,0 +1,39 @@
1
+ import {
2
+ createFormState,
3
+ required,
4
+ onlyIntegers,
5
+ hasLength,
6
+ matchesRegex
7
+ } from "redux-freeform";
8
+
9
+ //TODO: Will make zip code able to have more than 5 digits once we add in the FormattedInput because it will have issues with format of 60606-1111.
10
+
11
+ const formConfig = {
12
+ nameOnCard: {
13
+ validators: [required()]
14
+ },
15
+ creditCardNumber: {
16
+ validators: [required(), hasLength(15, 16)],
17
+ constraints: [onlyIntegers(), hasLength(0, 16)]
18
+ },
19
+ expirationDate: {
20
+ validators: [
21
+ required(),
22
+ hasLength(4, 4),
23
+ matchesRegex(/^(01|02|03|04|05|06|07|08|09|10|11|12)[2-9]\d$/)
24
+ ],
25
+ constraints: [onlyIntegers(), hasLength(0, 4)]
26
+ },
27
+ cvv: {
28
+ validators: [required(), hasLength(3, 4)],
29
+ constraints: [onlyIntegers(), hasLength(0, 4)]
30
+ },
31
+ zipCode: {
32
+ validators: [required(), hasLength(5, 5)],
33
+ constraints: [onlyIntegers(), hasLength(0, 5)]
34
+ }
35
+ };
36
+
37
+ export const { reducer, mapStateToProps, mapDispatchToProps } = createFormState(
38
+ formConfig
39
+ );
@@ -0,0 +1,11 @@
1
+ import PaymentFormCard from "./PaymentFormCard";
2
+ import {
3
+ reducer,
4
+ mapStateToProps,
5
+ mapDispatchToProps
6
+ } from "./PaymentFormCard.state";
7
+
8
+ PaymentFormCard.reducer = reducer;
9
+ PaymentFormCard.mapStateToProps = mapStateToProps;
10
+ PaymentFormCard.mapDispatchToProps = mapDispatchToProps;
11
+ export default PaymentFormCard;
@@ -7,6 +7,7 @@ import {
7
7
  FormContainer,
8
8
  FormInputColumn
9
9
  } from "../../atoms/form-layouts";
10
+ import { noop } from "../../../util/general";
10
11
 
11
12
  const PhoneForm = ({
12
13
  variant = "default",
@@ -14,7 +15,7 @@ const PhoneForm = ({
14
15
  actions,
15
16
  clearOnDismount,
16
17
  showErrors,
17
- handleSubmit
18
+ handleSubmit = noop
18
19
  }) => {
19
20
  if (clearOnDismount) {
20
21
  useEffect(() => () => actions.form.clear(), []);
@@ -2,7 +2,7 @@ import PhoneForm from "./PhoneForm";
2
2
  import {
3
3
  reducer,
4
4
  mapStateToProps,
5
- mapDispatchToProps,
5
+ mapDispatchToProps
6
6
  } from "./PhoneForm.state";
7
7
 
8
8
  PhoneForm.reducer = reducer;
@@ -103,7 +103,7 @@ const RadioSection = ({
103
103
  onKeyDown={e => handleKeyDown(section.id, e)}
104
104
  onFocus={() => setFocused(section.id)}
105
105
  onBlur={() => setFocused(null)}
106
- focusStyles={themeValues.focusStyles}
106
+ hoverStyles={themeValues.focusStyles}
107
107
  animate={openSection === section.id ? "open" : "closed"}
108
108
  initial={initiallyOpen ? "open" : "closed"}
109
109
  key={`item-${section.id}`}
@@ -12,12 +12,13 @@ import {
12
12
  import PasswordRequirements from "../../atoms/password-requirements";
13
13
  import { Box } from "../../atoms/layouts";
14
14
  import { FormInput, FormInputColumn } from "../../atoms/form-layouts";
15
+ import { noop } from "../../../util/general";
15
16
 
16
17
  const RegistrationForm = ({
17
18
  clearOnDismount,
18
19
  fields,
19
20
  actions,
20
- handleSubmit,
21
+ handleSubmit = noop,
21
22
  showErrors,
22
23
  isMobile
23
24
  }) => {
@@ -2,7 +2,7 @@ import RegistrationForm from "./RegistrationForm";
2
2
  import {
3
3
  reducer,
4
4
  mapStateToProps,
5
- mapDispatchToProps,
5
+ mapDispatchToProps
6
6
  } from "./RegistrationForm.state";
7
7
 
8
8
  RegistrationForm.reducer = reducer;
@@ -11,8 +11,10 @@ import {
11
11
  import PasswordRequirements from "../../atoms/password-requirements";
12
12
  import { Box } from "../../atoms/layouts";
13
13
  import { FormInput, FormInputColumn } from "../../atoms/form-layouts";
14
+ import { noop } from "../../../util/general";
15
+
14
16
  const ResetPasswordForm = ({
15
- handleSubmit,
17
+ handleSubmit = noop,
16
18
  clearOnDismount,
17
19
  fields,
18
20
  actions,
@@ -2,7 +2,7 @@ import ResetPasswordForm from "./ResetPasswordForm";
2
2
  import {
3
3
  reducer,
4
4
  mapStateToProps,
5
- mapDispatchToProps,
5
+ mapDispatchToProps
6
6
  } from "./ResetPasswordForm.state";
7
7
 
8
8
  ResetPasswordForm.reducer = reducer;
@@ -34,11 +34,16 @@ const TabSidebar = ({ links, isMobile, themeValues }) => (
34
34
  <InternalLink
35
35
  to={route}
36
36
  key={`${route}-${index}`}
37
- hoverStyles={
38
- active
39
- ? `> * {background-color: ${themeValues.activeTabHover};}`
40
- : `> * {background-color: rgba(8, 27, 43, 0.05);}`
41
- }
37
+ extraStyles={`&:hover, &:focus {
38
+ ${
39
+ active
40
+ ? `> * {
41
+ background-color: ${themeValues.activeTabHover};
42
+ }`
43
+ : `> * {
44
+ background-color: rgba(8, 27, 43, 0.05);
45
+ }`
46
+ }}`}
42
47
  >
43
48
  <Box
44
49
  padding={isMobile ? "6px 4px" : "18px 16px"}
@@ -24,7 +24,6 @@ const TermsAndConditionsModal = ({
24
24
  color={themeValues.linkColor}
25
25
  weight={themeValues.fontWeight}
26
26
  hoverStyles={themeValues.modalLinkHoverFocus}
27
- focusStyles={themeValues.modalLinkHoverFocus}
28
27
  extraStyles={`cursor: pointer;`}
29
28
  tabIndex="0"
30
29
  onKeyPress={e => e.key === "Enter" && toggleOpen(true)}
@@ -0,0 +1,4 @@
1
+ import * as colors from "./colors";
2
+ import * as fontWeights from "./style_constants";
3
+
4
+ export { colors, fontWeights };
package/src/index.js CHANGED
@@ -1,2 +1,4 @@
1
- // export * from "./deprecated";
2
1
  export * from "./components";
2
+ import * as constants from "./constants";
3
+ import * as util from "./util";
4
+ export { constants, util };
@@ -12,7 +12,7 @@ export const phoneFormats = [
12
12
  "(___) ___-_",
13
13
  "(___) ___-__",
14
14
  "(___) ___-___",
15
- "(___) ___-____",
15
+ "(___) ___-____"
16
16
  ];
17
17
 
18
18
  const zipFormats = [
@@ -25,6 +25,58 @@ const zipFormats = [
25
25
  "______",
26
26
  "_____-__",
27
27
  "_____-___",
28
- "_____-____",
28
+ "_____-____"
29
29
  ];
30
+
31
+ const creditCardFormats = [
32
+ "",
33
+ "_",
34
+ "__",
35
+ "___",
36
+ "____",
37
+ "____ _",
38
+ "____ __",
39
+ "____ ___",
40
+ "____ ____",
41
+ "____ ____ _",
42
+ "____ ____ __",
43
+ "____ ____ ___",
44
+ "____ ____ ____",
45
+ "____ ____ ____ _",
46
+ "____ ____ ____ __",
47
+ "____ ____ ____ ___",
48
+ "____ ____ ____ ____"
49
+ ];
50
+
51
+ export const moneyFormats = [
52
+ "",
53
+ "$0.0_",
54
+ "$0.__",
55
+ "$_.__",
56
+ "$__.__",
57
+ "$___.__",
58
+ "$_,___.__",
59
+ "$__,___.__",
60
+ "$___,___.__",
61
+ "$_,___,___.__",
62
+ "$__,___,___.__",
63
+ "$___,___,___.__",
64
+ "$_,___,___,___.__",
65
+ "$__,___,___,___.__",
66
+ "$___,___,___,___.__",
67
+ "$_,___,___,___,___.__"
68
+ ];
69
+
70
+ export const expirationDateFormats = ["", "_", "__/", "__/_", "__/__"];
71
+
30
72
  export const zipFormat = createFormat(zipFormats, formatDelimiter);
73
+ export const creditCardFormat = createFormat(
74
+ creditCardFormats,
75
+ formatDelimiter
76
+ );
77
+ export const expirationDateFormat = createFormat(
78
+ expirationDateFormats,
79
+ formatDelimiter
80
+ );
81
+ export const phoneFormat = createFormat(phoneFormats, formatDelimiter);
82
+ export const moneyFormat = createFormat(moneyFormats, formatDelimiter);
@@ -2,14 +2,37 @@ import numeral from "numeral";
2
2
 
3
3
  export const noop = () => {};
4
4
 
5
- const formatMoneyString = (s) => numeral(s).format("$0,0.00");
6
- const convertCentsToMoneyDecimal = (n) => (n / 100).toFixed(2);
7
- export const displayCurrency = (cents) =>
5
+ const formatMoneyString = s => numeral(s).format("$0,0.00");
6
+ const convertCentsToMoneyDecimal = n => (n / 100).toFixed(2);
7
+ export const displayCurrency = cents =>
8
8
  formatMoneyString(convertCentsToMoneyDecimal(cents));
9
9
 
10
+ export const convertCentsToMoneyInt = n => (n / 100).toFixed(0);
11
+
10
12
  export const safeChildren = (children, replacement = []) => {
11
13
  if (children && children instanceof Array) {
12
- return children.map((child) => (!child ? replacement : child));
14
+ return children.map(child => (!child ? replacement : child));
13
15
  }
14
16
  return !children ? replacement : children;
15
17
  };
18
+
19
+ export const generateClickHandler = (form, handleErrors, submitForm) => e => {
20
+ e.preventDefault();
21
+ const formHasError = Object.values(form.fields).reduce(
22
+ (acc, curr) => acc || curr.hasErrors,
23
+ false
24
+ );
25
+ return formHasError ? handleErrors() : submitForm();
26
+ };
27
+
28
+ export const checkCardBrand = number => {
29
+ if (/^6011/.test(number)) {
30
+ return "DISCOVER";
31
+ } else if (/^5[1-5]/.test(number)) {
32
+ return "MASTERCARD";
33
+ } else if (/^4/.test(number)) {
34
+ return "VISA";
35
+ } else if (/^3[4,7]/.test(number)) {
36
+ return "AMEX";
37
+ } else return "UNKNOWN";
38
+ };
@@ -0,0 +1,4 @@
1
+ import * as formats from "./formats";
2
+ import * as general from "./general";
3
+
4
+ export { formats, general };
@@ -1,22 +1,5 @@
1
- import * as S from "sanctuary";
2
- import * as $ from "sanctuary-def";
3
1
  import * as R from "ramda";
4
2
 
5
- // FormInput :: { form :: Object String any, keyName :: String }
6
-
7
- // String -> Maybe String
8
- const getsMaybeString = keyName => S.gets(S.is($.String))([keyName, "value"]);
9
-
10
- // String, String, Regex -> FormInput -> Either String String
11
- const regexTest = (keyName, errorMsg, regex) =>
12
- S.pipe([
13
- getsMaybeString(keyName),
14
- S.maybeToEither("Invalid string entered"),
15
- S.chain(string =>
16
- S.test(regex)(string) ? S.Right(string) : S.Left(errorMsg)
17
- )
18
- ]);
19
-
20
3
  // Constants to reprsent input error types
21
4
  export const MIN_LENGTH_ERROR = "error/HAS_LENGTH";
22
5
  export const MAX_LENGTH_ERROR = "max_length_error";
@@ -105,153 +88,3 @@ export const getInputState = (
105
88
  }
106
89
  return INPUT_STATE_VALID;
107
90
  };
108
-
109
- // Number -> FormInput -> Either String String
110
- export const stringHasMinLength = minLength => ({ form, keyName }) =>
111
- S.pipe([
112
- getsMaybeString(keyName),
113
- S.maybeToEither("Invalid string entered"),
114
- S.chain(string =>
115
- string.length >= minLength ? S.Right(string) : S.Left(MIN_LENGTH_ERROR)
116
- )
117
- ])(form);
118
-
119
- // Number -> FormInput -> Either String String
120
- // This function is INCLUSIVE of the maxLength arg supplied to it
121
- export const stringHasMaxLength = maxLength => ({ form, keyName }) =>
122
- S.pipe([
123
- getsMaybeString(keyName),
124
- S.maybeToEither("Invalid string entered"),
125
- S.chain(string =>
126
- string.length <= maxLength ? S.Right(string) : S.Left(MAX_LENGTH_ERROR)
127
- )
128
- ])(form);
129
-
130
- // Number -> FormInput -> Either String String
131
- export const stringHasExactLength = exactLength => ({ form, keyName }) =>
132
- S.pipe([
133
- getsMaybeString(keyName),
134
- S.maybeToEither("Invalid string entered"),
135
- S.chain(string =>
136
- string.length == exactLength
137
- ? S.Right(string)
138
- : S.Left(EXACT_LENGTH_ERROR)
139
- )
140
- ])(form);
141
-
142
- // Array(Number) -> FormInput -> Either String String
143
- export const stringHasMultipleValidLengths = validLengths => ({
144
- form,
145
- keyName
146
- }) =>
147
- S.pipe([
148
- getsMaybeString(keyName),
149
- S.maybeToEither("Form is missing key or value is not a string"),
150
- S.chain(string =>
151
- R.contains(string.length, validLengths)
152
- ? S.Right(string)
153
- : S.Left(MULTIPLE_LENGTHS_ERROR)
154
- )
155
- ])(form);
156
-
157
- // FormInput -> Either String String
158
- /* NOTE: this is not a foolproof email validation check
159
- It will likely fail on edge cases such as " "@foo.com
160
- It also cannot tell you if the particular email entered is valid for the given domain
161
- Or if the email account actually exists
162
- Only use this to help indicate to a user that they may have entered their email incorrectly
163
- Real validation should be accomplished on the server by sending an email to
164
- the provided email address
165
- */
166
- export const isProbablyAnEmail = ({ form, keyName }) =>
167
- regexTest(keyName, EMAIL_ERROR, /^[^\s@]+@[^\s@]+\.[^\s@]+$/)(form);
168
-
169
- // FormInput -> Either String String
170
- export const stringHasNumber = ({ form, keyName }) =>
171
- regexTest(keyName, HAS_NUMBER_ERROR, /[0-9]/)(form);
172
-
173
- // FormInput -> Either String String
174
- export const stringHasUppercaseLetter = ({ form, keyName }) =>
175
- regexTest(keyName, HAS_UPPERCASE_LETTER_ERROR, /[A-Z]/)(form);
176
-
177
- // FormInput -> Either String String
178
- export const stringHasLowercaseLetter = ({ form, keyName }) =>
179
- regexTest(keyName, HAS_LOWERCASE_LETTER_ERROR, /[a-z]/)(form);
180
-
181
- // FormInput -> Either String String
182
- export const stringHasSpecialCharacter = ({ form, keyName }) =>
183
- regexTest(keyName, HAS_SPECIAL_CHARACTER_ERROR, /[!@#$%^&*.?]/)(form);
184
-
185
- // FormInput -> Either String String
186
- export const stringIsOnlyNumbers = ({ form, keyName }) =>
187
- regexTest(keyName, ONLY_NUMBERS_ERROR, /^[0-9]+$/)(form);
188
-
189
- // FormInput -> Either String String
190
- export const stringIsOnlyLetters = ({ form, keyName }) =>
191
- regexTest(keyName, ONLY_LETTERS_ERROR, /^[a-zA-Z]+$/)(form);
192
-
193
- // fieldIsRequired asks for only one character,
194
- // which can be a letter (a-z) or (A-Z), a digit (0-9), or one of the special characters (!@#$%^&*.?)
195
- // If you need only letters or numbers use one of the more specific string checks above
196
- // If you need the string to exceed a specificed length, use stringHasMinLength
197
- // FormInput -> Either String String
198
- export const fieldIsRequired = ({ form, keyName }) =>
199
- S.pipe([
200
- getsMaybeString(keyName),
201
- S.maybeToEither("Invalid string entered"),
202
- S.chain(string =>
203
- string.replace(/\s/g, "").length === 0
204
- ? S.Left(REQUIRED_FIELD_ERROR)
205
- : S.Right(string)
206
- )
207
- ])(form);
208
-
209
- // Number -> FormInput -> Either String String
210
- export const numberIsGreaterThan = minValue => ({ form, keyName }) =>
211
- S.pipe([
212
- getsMaybeString(keyName),
213
- S.maybeToEither("Invalid string entered"),
214
- S.chain(n =>
215
- parseInt(n, 10) > minValue
216
- ? S.Right(n.toString())
217
- : S.Left(NUM_GREATER_THAN_ERROR)
218
- )
219
- ])(form);
220
-
221
- // Number -> FormInput -> Either String String
222
- export const numberIsLessThan = maxValue => ({ form, keyName }) =>
223
- S.pipe([
224
- getsMaybeString(keyName),
225
- S.maybeToEither("Invalid string entered"),
226
- S.chain(n =>
227
- parseInt(n, 10) < maxValue
228
- ? S.Right(n.toString())
229
- : S.Left(NUM_LESS_THAN_ERROR)
230
- )
231
- ])(form);
232
-
233
- // Error message splits the keyname on capital letters, joins with a space, and lower cases, so "accountNumber" becomes "account number"
234
- // Eq a => FormInput -> String -> Either String a
235
- export const matchesOtherField = matchKey => ({ form, keyName }) =>
236
- S.equals(S.props([keyName, "value"])(form))(
237
- S.props([matchKey, "value"])(form)
238
- )
239
- ? S.Right(S.props([keyName, "value"])(form))
240
- : S.Left(MATCHES_FIELD_ERROR);
241
-
242
- // Array(String) -> FormInput -> Either String String
243
- export const selectIsValid = validOptions => ({ form, keyName }) =>
244
- S.pipe([
245
- getsMaybeString(keyName),
246
- S.maybeToEither("Invalid string entered"),
247
- S.chain(string =>
248
- R.contains(S.toUpper(string), validOptions)
249
- ? S.Right(string)
250
- : S.Left(VALID_SELECT_OPTION_ERROR)
251
- )
252
- ])(form);
253
-
254
- export const isInputInvalid = input => input.error && input.showError;
255
-
256
- export const inputErrorMessage = input =>
257
- input.showError ? input.errorMsg : "";