dce-reactkit 3.4.0-beta.9 → 3.4.0

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 (128) hide show
  1. package/.eslintrc.js +0 -2
  2. package/dist/cjs/index.js +190 -36371
  3. package/dist/cjs/index.js.map +1 -1
  4. package/dist/cjs/types/helpers/abbreviate.test.d.ts +5 -0
  5. package/dist/cjs/types/helpers/avg.test.d.ts +5 -0
  6. package/dist/cjs/types/helpers/canReviewLogs.d.ts +5 -0
  7. package/dist/cjs/types/helpers/canReviewLogs.test.d.ts +5 -0
  8. package/dist/cjs/types/helpers/ceilToNumDecimals.test.d.ts +5 -0
  9. package/dist/cjs/types/helpers/compareArraysByProp.test.d.ts +5 -0
  10. package/dist/cjs/types/helpers/extractProp.test.d.ts +5 -0
  11. package/dist/cjs/types/helpers/floorToNumDecimals.test.d.ts +5 -0
  12. package/dist/cjs/types/helpers/forceNumIntoBounds.test.d.ts +5 -0
  13. package/dist/cjs/types/helpers/genCSV.test.d.ts +5 -0
  14. package/dist/cjs/types/helpers/genCommaList.d.ts +12 -0
  15. package/dist/cjs/types/helpers/genCommaList.test.d.ts +1 -0
  16. package/dist/cjs/types/helpers/getHumanReadableDate.test.d.ts +5 -0
  17. package/dist/cjs/types/helpers/getLocalTimeInfo.test.d.ts +6 -0
  18. package/dist/cjs/types/helpers/getMonthName.test.d.ts +5 -0
  19. package/dist/cjs/types/helpers/getOrdinal.test.d.ts +5 -0
  20. package/dist/cjs/types/helpers/getPartOfDay.test.d.ts +5 -0
  21. package/dist/cjs/types/helpers/getTimeInfoInET.test.d.ts +5 -0
  22. package/dist/cjs/types/helpers/validators/shared/constants/ERROR_MESSAGES.d.ts +10 -0
  23. package/dist/cjs/types/helpers/validators/shared/helpers/validateRegex.d.ts +19 -0
  24. package/dist/cjs/types/helpers/validators/shared/types/ValidationResult.d.ts +12 -0
  25. package/dist/cjs/types/helpers/validators/validateEmail.d.ts +11 -0
  26. package/dist/cjs/types/helpers/validators/validateEmail.test.d.ts +1 -0
  27. package/dist/cjs/types/helpers/validators/validatePhoneNumber.d.ts +11 -0
  28. package/dist/cjs/types/helpers/validators/validatePhoneNumber.test.d.ts +1 -0
  29. package/dist/cjs/types/helpers/validators/validateString.d.ts +22 -0
  30. package/dist/cjs/types/helpers/validators/validateString.test.d.ts +1 -0
  31. package/dist/cjs/types/index.d.ts +1 -6
  32. package/dist/esm/index.js +419 -36579
  33. package/dist/esm/index.js.map +1 -1
  34. package/dist/esm/types/helpers/abbreviate.test.d.ts +5 -0
  35. package/dist/esm/types/helpers/avg.test.d.ts +5 -0
  36. package/dist/esm/types/helpers/canReviewLogs.d.ts +5 -0
  37. package/dist/esm/types/helpers/canReviewLogs.test.d.ts +5 -0
  38. package/dist/esm/types/helpers/ceilToNumDecimals.test.d.ts +5 -0
  39. package/dist/esm/types/helpers/compareArraysByProp.test.d.ts +5 -0
  40. package/dist/esm/types/helpers/extractProp.test.d.ts +5 -0
  41. package/dist/esm/types/helpers/floorToNumDecimals.test.d.ts +5 -0
  42. package/dist/esm/types/helpers/forceNumIntoBounds.test.d.ts +5 -0
  43. package/dist/esm/types/helpers/genCSV.test.d.ts +5 -0
  44. package/dist/esm/types/helpers/genCommaList.d.ts +12 -0
  45. package/dist/esm/types/helpers/genCommaList.test.d.ts +1 -0
  46. package/dist/esm/types/helpers/getHumanReadableDate.test.d.ts +5 -0
  47. package/dist/esm/types/helpers/getLocalTimeInfo.test.d.ts +6 -0
  48. package/dist/esm/types/helpers/getMonthName.test.d.ts +5 -0
  49. package/dist/esm/types/helpers/getOrdinal.test.d.ts +5 -0
  50. package/dist/esm/types/helpers/getPartOfDay.test.d.ts +5 -0
  51. package/dist/esm/types/helpers/getTimeInfoInET.test.d.ts +5 -0
  52. package/dist/esm/types/helpers/validators/shared/constants/ERROR_MESSAGES.d.ts +10 -0
  53. package/dist/esm/types/helpers/validators/shared/helpers/validateRegex.d.ts +19 -0
  54. package/dist/esm/types/helpers/validators/shared/types/ValidationResult.d.ts +12 -0
  55. package/dist/esm/types/helpers/validators/validateEmail.d.ts +11 -0
  56. package/dist/esm/types/helpers/validators/validateEmail.test.d.ts +1 -0
  57. package/dist/esm/types/helpers/validators/validatePhoneNumber.d.ts +11 -0
  58. package/dist/esm/types/helpers/validators/validatePhoneNumber.test.d.ts +1 -0
  59. package/dist/esm/types/helpers/validators/validateString.d.ts +22 -0
  60. package/dist/esm/types/helpers/validators/validateString.test.d.ts +1 -0
  61. package/dist/esm/types/index.d.ts +1 -6
  62. package/dist/index.d.ts +35 -166
  63. package/package.json +13 -17
  64. package/src/client/initClient.tsx +20 -12
  65. package/src/components/AppWrapper.tsx +1 -1
  66. package/src/components/ItemPicker/index.test.tsx +10 -10
  67. package/src/helpers/abbreviate.test.ts +26 -0
  68. package/src/helpers/avg.test.ts +24 -0
  69. package/src/helpers/canReviewLogs.test.ts +67 -0
  70. package/src/helpers/canReviewLogs.ts +20 -0
  71. package/src/helpers/ceilToNumDecimals.test.ts +39 -0
  72. package/src/helpers/compareArraysByProp.test.ts +102 -0
  73. package/src/helpers/extractProp.test.ts +34 -0
  74. package/src/helpers/floorToNumDecimals.test.ts +39 -0
  75. package/src/helpers/forceNumIntoBounds.test.ts +34 -0
  76. package/src/helpers/genCSV.test.ts +61 -0
  77. package/src/helpers/genCSV.ts +5 -2
  78. package/src/helpers/genCommaList.test.ts +57 -0
  79. package/src/helpers/genCommaList.ts +21 -0
  80. package/src/helpers/getHumanReadableDate.test.ts +29 -0
  81. package/src/helpers/getLocalTimeInfo.test.ts +48 -0
  82. package/src/helpers/getMonthName.test.ts +79 -0
  83. package/src/helpers/getOrdinal.test.ts +84 -0
  84. package/src/helpers/{getOrdinal.tsx → getOrdinal.ts} +1 -0
  85. package/src/helpers/getPartOfDay.test.ts +56 -0
  86. package/src/helpers/getTimeInfoInET.test.ts +72 -0
  87. package/src/helpers/validators/shared/constants/ERROR_MESSAGES.ts +18 -0
  88. package/src/helpers/validators/shared/helpers/validateRegex.ts +47 -0
  89. package/src/helpers/validators/shared/types/ValidationResult.ts +20 -0
  90. package/src/helpers/validators/validateEmail.test.ts +110 -0
  91. package/src/helpers/validators/validateEmail.ts +47 -0
  92. package/src/helpers/validators/validatePhoneNumber.test.ts +94 -0
  93. package/src/helpers/validators/validatePhoneNumber.ts +49 -0
  94. package/src/helpers/validators/validateString.test.ts +175 -0
  95. package/src/helpers/validators/validateString.ts +171 -0
  96. package/src/index.ts +10 -12
  97. package/tsconfig.json +6 -2
  98. package/dist/cjs/types/components/DBEntryManagerPanel/AddOrEditDBEntry/CreatableMultiselect.d.ts +0 -10
  99. package/dist/cjs/types/components/DBEntryManagerPanel/AddOrEditDBEntry/index.d.ts +0 -34
  100. package/dist/cjs/types/components/DBEntryManagerPanel/helpers/generateEndpointPath.d.ts +0 -9
  101. package/dist/cjs/types/components/DBEntryManagerPanel/index.d.ts +0 -25
  102. package/dist/cjs/types/components/DBEntryManagerPanel/types/DBEntry.d.ts +0 -8
  103. package/dist/cjs/types/components/DBEntryManagerPanel/types/DBEntryField.d.ts +0 -49
  104. package/dist/cjs/types/components/DBEntryManagerPanel/types/DBEntryFieldType.d.ts +0 -12
  105. package/dist/cjs/types/helpers/addDBEditorEndpoints.d.ts +0 -40
  106. package/dist/esm/types/components/DBEntryManagerPanel/AddOrEditDBEntry/CreatableMultiselect.d.ts +0 -10
  107. package/dist/esm/types/components/DBEntryManagerPanel/AddOrEditDBEntry/index.d.ts +0 -34
  108. package/dist/esm/types/components/DBEntryManagerPanel/helpers/generateEndpointPath.d.ts +0 -9
  109. package/dist/esm/types/components/DBEntryManagerPanel/index.d.ts +0 -25
  110. package/dist/esm/types/components/DBEntryManagerPanel/types/DBEntry.d.ts +0 -8
  111. package/dist/esm/types/components/DBEntryManagerPanel/types/DBEntryField.d.ts +0 -49
  112. package/dist/esm/types/components/DBEntryManagerPanel/types/DBEntryFieldType.d.ts +0 -12
  113. package/dist/esm/types/helpers/addDBEditorEndpoints.d.ts +0 -40
  114. package/src/components/DBEntryManagerPanel/AddOrEditDBEntry/CreatableMultiselect.tsx +0 -286
  115. package/src/components/DBEntryManagerPanel/AddOrEditDBEntry/index.tsx +0 -722
  116. package/src/components/DBEntryManagerPanel/helpers/generateEndpointPath.ts +0 -15
  117. package/src/components/DBEntryManagerPanel/index.tsx +0 -511
  118. package/src/components/DBEntryManagerPanel/types/DBEntry.ts +0 -7
  119. package/src/components/DBEntryManagerPanel/types/DBEntryField.ts +0 -94
  120. package/src/components/DBEntryManagerPanel/types/DBEntryFieldType.ts +0 -18
  121. package/src/helpers/addDBEditorEndpoints.ts +0 -124
  122. /package/src/helpers/{abbreviate.tsx → abbreviate.ts} +0 -0
  123. /package/src/helpers/{avg.tsx → avg.ts} +0 -0
  124. /package/src/helpers/{ceilToNumDecimals.tsx → ceilToNumDecimals.ts} +0 -0
  125. /package/src/helpers/{floorToNumDecimals.tsx → floorToNumDecimals.ts} +0 -0
  126. /package/src/helpers/{forceNumIntoBounds.tsx → forceNumIntoBounds.ts} +0 -0
  127. /package/src/helpers/{getLocalTimeInfo.tsx → getLocalTimeInfo.ts} +0 -0
  128. /package/src/helpers/{getTimeInfoInET.tsx → getTimeInfoInET.ts} +0 -0
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Test file for the getOrdinal helper
3
+ * @author Gabe Abrams
4
+ */
5
+
6
+ // Import main function
7
+ import getOrdinal from './getOrdinal';
8
+
9
+ describe('getOrdinal', () => {
10
+ it('should return "th" for 0', () => {
11
+ const result = getOrdinal(0);
12
+ expect(result).toEqual('th');
13
+ });
14
+
15
+ it('should return "st" for 1', () => {
16
+ const result = getOrdinal(1);
17
+ expect(result).toEqual('st');
18
+ });
19
+
20
+ it('should return "nd" for 2', () => {
21
+ const result = getOrdinal(2);
22
+ expect(result).toEqual('nd');
23
+ });
24
+
25
+ it('should return "rd" for 3', () => {
26
+ const result = getOrdinal(3);
27
+ expect(result).toEqual('rd');
28
+ });
29
+
30
+ it('should return "th" for 4', () => {
31
+ const result = getOrdinal(4);
32
+ expect(result).toEqual('th');
33
+ });
34
+
35
+ it('should return "th" for 11', () => {
36
+ const result = getOrdinal(11);
37
+ expect(result).toEqual('th');
38
+ });
39
+
40
+ it('should return "st" for 21', () => {
41
+ const result = getOrdinal(21);
42
+ expect(result).toEqual('st');
43
+ });
44
+
45
+ it('should return "nd" for 22', () => {
46
+ const result = getOrdinal(22);
47
+ expect(result).toEqual('nd');
48
+ });
49
+
50
+ it('should return "rd" for 23', () => {
51
+ const result = getOrdinal(23);
52
+ expect(result).toEqual('rd');
53
+ });
54
+
55
+ it('should return "th" for 24', () => {
56
+ const result = getOrdinal(24);
57
+ expect(result).toEqual('th');
58
+ });
59
+
60
+ it('should return "th" for 100', () => {
61
+ const result = getOrdinal(100);
62
+ expect(result).toEqual('th');
63
+ });
64
+
65
+ it('should return "st" for 101', () => {
66
+ const result = getOrdinal(101);
67
+ expect(result).toEqual('st');
68
+ });
69
+
70
+ it('should return "nd" for 102', () => {
71
+ const result = getOrdinal(102);
72
+ expect(result).toEqual('nd');
73
+ });
74
+
75
+ it('should return "rd" for 103', () => {
76
+ const result = getOrdinal(103);
77
+ expect(result).toEqual('rd');
78
+ });
79
+
80
+ it('should return "th" for 104', () => {
81
+ const result = getOrdinal(104);
82
+ expect(result).toEqual('th');
83
+ });
84
+ });
@@ -1,3 +1,4 @@
1
+ // Constants
1
2
  const ORDINALS = ['th', 'st', 'nd', 'rd'];
2
3
 
3
4
  /**
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Test file for the getPartOfDay helper
3
+ * @author Gabe Abrams
4
+ */
5
+
6
+ import getPartOfDay from './getPartOfDay';
7
+
8
+ describe('getPartOfDay', () => {
9
+ it('should return "morning" if the current hour is less than 12', () => {
10
+ // Mock the current hour to be 9am
11
+ jest.spyOn(Date.prototype, 'getHours').mockReturnValue(9);
12
+
13
+ const result = getPartOfDay();
14
+ expect(result).toEqual('morning');
15
+ });
16
+
17
+ it('should return "afternoon" if the current hour is between 12 and 16 (inclusive)', () => {
18
+ // Mock the current hour to be 2pm
19
+ jest.spyOn(Date.prototype, 'getHours').mockReturnValue(14);
20
+
21
+ const result = getPartOfDay();
22
+ expect(result).toEqual('afternoon');
23
+ });
24
+
25
+ it('should return "evening" if the current hour is between 17 and 24 (inclusive)', () => {
26
+ // Mock the current hour to be 8pm
27
+ jest.spyOn(Date.prototype, 'getHours').mockReturnValue(20);
28
+
29
+ const result = getPartOfDay();
30
+ expect(result).toEqual('evening');
31
+ });
32
+
33
+ it('should return "evening" if the current hour is 17', () => {
34
+ // Mock the current hour to be 5pm
35
+ jest.spyOn(Date.prototype, 'getHours').mockReturnValue(17);
36
+
37
+ const result = getPartOfDay();
38
+ expect(result).toEqual('evening');
39
+ });
40
+
41
+ it('should return "evening" if the current hour is 24', () => {
42
+ // Mock the current hour to be midnight
43
+ jest.spyOn(Date.prototype, 'getHours').mockReturnValue(24);
44
+
45
+ const result = getPartOfDay();
46
+ expect(result).toEqual('evening');
47
+ });
48
+
49
+ it('should return "morning" if the current hour is 0', () => {
50
+ // Mock the current hour to be midnight
51
+ jest.spyOn(Date.prototype, 'getHours').mockReturnValue(0);
52
+
53
+ const result = getPartOfDay();
54
+ expect(result).toEqual('morning');
55
+ });
56
+ });
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Test file for the getTimeInfoInET helper
3
+ * @author Gabe Abrams
4
+ */
5
+
6
+ // Import main function
7
+ import getTimeInfoInET from './getTimeInfoInET';
8
+
9
+ describe('getTimeInfoInET', () => {
10
+ it('should return the current time info in ET if no argument is passed', () => {
11
+ const result = getTimeInfoInET();
12
+ const now = new Date();
13
+ const expectedTimestamp = now.getTime();
14
+ const expectedYear = now.getFullYear();
15
+ const expectedMonth = now.getMonth() + 1;
16
+ const expectedDay = now.getDate();
17
+ const expectedHour = now.getHours();
18
+ const expectedHour12 = expectedHour % 12 || 12;
19
+ const expectedMinute = now.getMinutes();
20
+ const expectedIsPM = expectedHour >= 12;
21
+ expect(result.timestamp).toBeCloseTo(expectedTimestamp, -2);
22
+ expect(result.year).toEqual(expectedYear);
23
+ expect(result.month).toEqual(expectedMonth);
24
+ expect(result.day).toEqual(expectedDay);
25
+ expect(result.hour).toEqual(expectedHour);
26
+ expect(result.hour12).toEqual(expectedHour12);
27
+ expect(result.minute).toEqual(expectedMinute);
28
+ expect(result.isPM).toEqual(expectedIsPM);
29
+ });
30
+
31
+ it('should return the time info in ET for a specified date', () => {
32
+ const date = new Date('2022-01-01T00:00:00Z');
33
+ const result = getTimeInfoInET(date);
34
+ const expectedTimestamp = date.getTime();
35
+ const expectedYear = 2021;
36
+ const expectedMonth = 12;
37
+ const expectedDay = 31;
38
+ const expectedHour = 19;
39
+ const expectedHour12 = 7;
40
+ const expectedMinute = 0;
41
+ const expectedIsPM = false;
42
+ expect(result.timestamp).toEqual(expectedTimestamp);
43
+ expect(result.year).toEqual(expectedYear);
44
+ expect(result.month).toEqual(expectedMonth);
45
+ expect(result.day).toEqual(expectedDay);
46
+ expect(result.hour).toEqual(expectedHour);
47
+ expect(result.hour12).toEqual(expectedHour12);
48
+ expect(result.minute).toEqual(expectedMinute);
49
+ expect(result.isPM).toEqual(expectedIsPM);
50
+ });
51
+
52
+ it('should return the time info in ET for a specified timestamp', () => {
53
+ const timestamp = 1640995200000; // 2022-01-01T00:00:00Z
54
+ const result = getTimeInfoInET(timestamp);
55
+ const expectedTimestamp = timestamp;
56
+ const expectedYear = 2021;
57
+ const expectedMonth = 12;
58
+ const expectedDay = 31;
59
+ const expectedHour = 19;
60
+ const expectedHour12 = 7;
61
+ const expectedMinute = 0;
62
+ const expectedIsPM = false;
63
+ expect(result.timestamp).toEqual(expectedTimestamp);
64
+ expect(result.year).toEqual(expectedYear);
65
+ expect(result.month).toEqual(expectedMonth);
66
+ expect(result.day).toEqual(expectedDay);
67
+ expect(result.hour).toEqual(expectedHour);
68
+ expect(result.hour12).toEqual(expectedHour12);
69
+ expect(result.minute).toEqual(expectedMinute);
70
+ expect(result.isPM).toEqual(expectedIsPM);
71
+ });
72
+ });
@@ -0,0 +1,18 @@
1
+ /*------------------------------------------------------------------------*/
2
+ /* ------------------------------ Constants ----------------------------- */
3
+ /*------------------------------------------------------------------------*/
4
+
5
+ export const INVALID_REGEX_ERROR = 'input does not follow the requested format';
6
+ export const INVALID_EMAIL_ERROR = 'Please provide a valid email address.';
7
+ export const INVALID_PHONE_ERROR = 'Please provide a valid phone number.';
8
+ export const INVALID_STRING_ERRORS = {
9
+ MIN_LEN: (minLen: number) => {
10
+ return `input must not be under ${minLen} character(s)`;
11
+ },
12
+ MAX_LEN: (maxLen: number) => {
13
+ return `input must not be over ${maxLen} character(s)`;
14
+ },
15
+ LETTERS_ONLY: 'input must only contain letters',
16
+ NUMBERS_ONLY: 'input must only contain numbers',
17
+ MESSAGE_INTRO: 'The following error(s) occurred: ',
18
+ };
@@ -0,0 +1,47 @@
1
+ // Import types
2
+ import ValidationResult from '../types/ValidationResult';
3
+
4
+ // Import constants
5
+ import { INVALID_REGEX_ERROR } from '../constants/ERROR_MESSAGES';
6
+
7
+ /**
8
+ * Determines whether a given input string is considered valid based on
9
+ * the provided regex.
10
+ * @author Austen Money
11
+ * @param opts object containing all args
12
+ * @param opts.input user-provided input to validate
13
+ * @param opts.regex regular expression to check against input
14
+ * @param [opts.regexDescription] description of what regexString is checking
15
+ * for, used to customize error message
16
+ * @returns the unchanged input if valid, or a customized error message if
17
+ * invalid
18
+ */
19
+ const validateRegex = (
20
+ opts: {
21
+ input: string,
22
+ regex: RegExp,
23
+ regexDescription?: string,
24
+ },
25
+ ): ValidationResult<string> => {
26
+ // customize error message in case of invalid input
27
+ const errorMessage = `${INVALID_REGEX_ERROR}${
28
+ opts.regexDescription
29
+ ? ': '
30
+ : ''
31
+ }${opts.regexDescription}`;
32
+
33
+ // return error message if test is invalid, or input string if valid
34
+ return (
35
+ opts.regex.test(opts.input)
36
+ ? {
37
+ isValid: true,
38
+ cleanedValue: opts.input,
39
+ }
40
+ : {
41
+ isValid: false,
42
+ errorMessage,
43
+ }
44
+ );
45
+ };
46
+
47
+ export default validateRegex;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Result of a validation function.
3
+ * @author Austen Money
4
+ */
5
+ type ValidationResult<CleanedValueType> = (
6
+ | {
7
+ // if true, the validation was successful
8
+ isValid: true,
9
+ // the cleaned version of the value
10
+ cleanedValue: CleanedValueType,
11
+ }
12
+ | {
13
+ // if false, the validation was not successful
14
+ isValid: false,
15
+ // describes why the validation failed
16
+ errorMessage: string,
17
+ }
18
+ );
19
+
20
+ export default ValidationResult;
@@ -0,0 +1,110 @@
1
+ // Import types
2
+ import ValidationResult from './shared/types/ValidationResult';
3
+
4
+ // Import constants
5
+ import { INVALID_EMAIL_ERROR } from './shared/constants/ERROR_MESSAGES';
6
+
7
+ // Import function
8
+ import validateEmail from './validateEmail';
9
+
10
+ /*------------------------------------------------------------------------*/
11
+ /* ---------------------------- Valid Tests --------------------------- */
12
+ /*------------------------------------------------------------------------*/
13
+
14
+ const validEmailPairs: { input: string, cleaned: string }[] = [
15
+ {
16
+ input: 'username@gmail.com',
17
+ cleaned: 'username@gmail.com',
18
+ },
19
+ {
20
+ input: 'u@hotmail.org',
21
+ cleaned: 'u@hotmail.org',
22
+ },
23
+ {
24
+ input: ' username@gmail.com',
25
+ cleaned: 'username@gmail.com',
26
+ },
27
+ {
28
+ input: 'username@gmail.com ',
29
+ cleaned: 'username@gmail.com',
30
+ },
31
+ {
32
+ input: ' username@gmail.com ',
33
+ cleaned: 'username@gmail.com',
34
+ },
35
+ {
36
+ input: 'username@harvard.edu',
37
+ cleaned: 'username@harvard.edu',
38
+ },
39
+ {
40
+ input: 'username@harvard.subdomain.edu',
41
+ cleaned: 'username@harvard.subdomain.edu',
42
+ },
43
+ {
44
+ input: '\nusername@harvard.subdomain.edu',
45
+ cleaned: 'username@harvard.subdomain.edu',
46
+ },
47
+ {
48
+ input: '\n\tusername@harvard.subdomain.edu',
49
+ cleaned: 'username@harvard.subdomain.edu',
50
+ },
51
+ {
52
+ input: 'username@harvard.subdomain.edu\n',
53
+ cleaned: 'username@harvard.subdomain.edu',
54
+ },
55
+ {
56
+ input: 'username@harvard.subdomain.edu\n\t',
57
+ cleaned: 'username@harvard.subdomain.edu',
58
+ },
59
+ ];
60
+
61
+ test(
62
+ 'Returns true for a given valid email and removes any leading or trailing whitespace.',
63
+ async () => {
64
+ validEmailPairs.forEach((pair) => {
65
+ const validResponse: ValidationResult<string> = {
66
+ isValid: true,
67
+ cleanedValue: pair.cleaned,
68
+ };
69
+
70
+ expect(validateEmail(pair.input)).toStrictEqual(validResponse);
71
+ });
72
+ },
73
+ );
74
+
75
+ /*------------------------------------------------------------------------*/
76
+ /* ---------------------------- Invalid Tests --------------------------- */
77
+ /*------------------------------------------------------------------------*/
78
+
79
+ const invalidEmails: string[] = [
80
+ '',
81
+ ' ',
82
+ 'gmail.com',
83
+ '@gmail.com ',
84
+ 'username',
85
+ 'username@.com',
86
+ 'user name@gmail.com',
87
+ 'username @gmail.com',
88
+ 'username@ gmail.com',
89
+ 'username@gmail .com',
90
+ 'username\t@gmail.com',
91
+ 'username\n@gmail.com',
92
+ 'username@gmail\n.com',
93
+ 'username@gmail.\tcom',
94
+ 'user\nname@gmail.com',
95
+ 'user\tname@gmail.com',
96
+ ];
97
+
98
+ const invalidResponse: ValidationResult<string> = {
99
+ isValid: false,
100
+ errorMessage: INVALID_EMAIL_ERROR,
101
+ };
102
+
103
+ test(
104
+ 'Returns false for a given invalid email.',
105
+ async () => {
106
+ invalidEmails.forEach((email) => {
107
+ expect(validateEmail(email)).toStrictEqual(invalidResponse);
108
+ });
109
+ },
110
+ );
@@ -0,0 +1,47 @@
1
+ // Import helpers
2
+ import validateRegex from './shared/helpers/validateRegex';
3
+
4
+ // Import constants
5
+ import { INVALID_EMAIL_ERROR } from './shared/constants/ERROR_MESSAGES';
6
+
7
+ // Import types
8
+ import ValidationResult from './shared/types/ValidationResult';
9
+
10
+ /**
11
+ * Determines whether a given email address is valid.
12
+ * @author Austen Money
13
+ * @param email email address to validate
14
+ * @returns whether email fulfills proper formatting requirements, includes a
15
+ * cleaned version of the address without leading or trailing
16
+ * whitespace if valid or an error message if invalid.
17
+ */
18
+ const validateEmail = (
19
+ email: string,
20
+ ): ValidationResult<string> => {
21
+ // validation regex, sourced from HTML living standard: http://www.whatwg.org/specs/web-apps/current-work/multipage/forms.html#e-mail-state-(type=email)
22
+ // eslint-disable-next-line max-len
23
+ const emailRegex = /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
24
+
25
+ // remove leading and trailing whitespace
26
+ const cleanedValue: string = email.replace(/^\s+|\s+$/g, '');
27
+
28
+ // validate email with regex, and return error if not valid
29
+ return (
30
+ validateRegex(
31
+ {
32
+ input: cleanedValue,
33
+ regex: emailRegex,
34
+ },
35
+ ).isValid
36
+ ? {
37
+ isValid: true,
38
+ cleanedValue,
39
+ }
40
+ : {
41
+ isValid: false,
42
+ errorMessage: INVALID_EMAIL_ERROR,
43
+ }
44
+ );
45
+ };
46
+
47
+ export default validateEmail;
@@ -0,0 +1,94 @@
1
+ // Import types
2
+ import ValidationResult from './shared/types/ValidationResult';
3
+
4
+ // Import constants
5
+ import { INVALID_PHONE_ERROR } from './shared/constants/ERROR_MESSAGES';
6
+
7
+ // Import function
8
+ import validatePhoneNumber from './validatePhoneNumber';
9
+
10
+ /*------------------------------------------------------------------------*/
11
+ /* ---------------------------- Valid Tests --------------------------- */
12
+ /*------------------------------------------------------------------------*/
13
+
14
+ const cleanedNumber = '1234567890';
15
+ const cleanedNumberExt = '11234567890';
16
+
17
+ const validPhoneNumbers: { input: string, cleaned: string }[] = [
18
+ {
19
+ input: '1234567890',
20
+ cleaned: cleanedNumber,
21
+ },
22
+ {
23
+ input: '(123)-456-7890',
24
+ cleaned: cleanedNumber,
25
+ },
26
+ {
27
+ input: '123-456-7890',
28
+ cleaned: cleanedNumber,
29
+ },
30
+ {
31
+ input: '(123)456-7890',
32
+ cleaned: cleanedNumber,
33
+ },
34
+ {
35
+ input: '(123)4567890',
36
+ cleaned: cleanedNumber,
37
+ },
38
+ {
39
+ input: ' 123456-7890 ',
40
+ cleaned: cleanedNumber,
41
+ },
42
+ {
43
+ input: '+1 123 456 7890',
44
+ cleaned: cleanedNumberExt,
45
+ },
46
+ {
47
+ input: '+1123456 7890',
48
+ cleaned: cleanedNumberExt,
49
+ },
50
+ {
51
+ input: '+1(123)456-7890',
52
+ cleaned: cleanedNumberExt,
53
+ },
54
+ ];
55
+
56
+ test(
57
+ 'Returns true for a given valid phone number and removes any non-digit characters.',
58
+ async () => {
59
+ validPhoneNumbers.forEach((pair) => {
60
+ const validResponse: ValidationResult<string> = {
61
+ isValid: true,
62
+ cleanedValue: pair.cleaned,
63
+ };
64
+
65
+ expect(validatePhoneNumber(pair.input)).toStrictEqual(validResponse);
66
+ });
67
+ },
68
+ );
69
+
70
+ /*------------------------------------------------------------------------*/
71
+ /* ---------------------------- Invalid Tests --------------------------- */
72
+ /*------------------------------------------------------------------------*/
73
+
74
+ const invalidPhoneNumbers: string[] = [
75
+ '123456789',
76
+ '1234567894567890',
77
+ '1234-567-890',
78
+ '123456m78',
79
+ '123 4567 890',
80
+ ];
81
+
82
+ const invalidResponse: ValidationResult<string> = {
83
+ isValid: false,
84
+ errorMessage: INVALID_PHONE_ERROR,
85
+ };
86
+
87
+ test(
88
+ 'Returns false for a given valid phone number.',
89
+ async () => {
90
+ invalidPhoneNumbers.forEach((phoneNumber) => {
91
+ expect(validatePhoneNumber(phoneNumber)).toStrictEqual(invalidResponse);
92
+ });
93
+ },
94
+ );
@@ -0,0 +1,49 @@
1
+ // Import helpers
2
+ import validateRegex from './shared/helpers/validateRegex';
3
+
4
+ // Import constants
5
+ import { INVALID_PHONE_ERROR } from './shared/constants/ERROR_MESSAGES';
6
+
7
+ // Import types
8
+ import ValidationResult from './shared/types/ValidationResult';
9
+
10
+ /**
11
+ * Determines whether a given phone number is valid.
12
+ * @author Austen Money
13
+ * @param phoneNumber phone number to validate
14
+ * @returns whether phone number is considered valid - if valid, also returns
15
+ * a cleaned version of the number without any formatting. If invalid,
16
+ * returns an error message.
17
+ */
18
+ const validatePhoneNumber = (
19
+ phoneNumber: string,
20
+ ): ValidationResult<string> => {
21
+ // regex to validate phone number
22
+ const validationRegex = /^\s*(?:\+?(\d{1,3}))?[-. (]*(\d{3})[-. )]*(\d{3})[-. ]*(\d{4})(?: *x(\d+))?\s*$/;
23
+
24
+ // validate phone number with regex
25
+ const validationResponse = validateRegex(
26
+ {
27
+ input: phoneNumber,
28
+ regex: validationRegex,
29
+ },
30
+ );
31
+
32
+ // remove all non-digits from number
33
+ const cleanedValue: string = phoneNumber.replace(/\D/g, '');
34
+
35
+ // return cleaned value if valid, or error message if invalid
36
+ return (
37
+ validationResponse.isValid
38
+ ? {
39
+ isValid: true,
40
+ cleanedValue,
41
+ }
42
+ : {
43
+ isValid: false,
44
+ errorMessage: INVALID_PHONE_ERROR,
45
+ }
46
+ );
47
+ };
48
+
49
+ export default validatePhoneNumber;