@elliemae/loan-field-renderers 26.2.2

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 (225) hide show
  1. package/dist/cjs/ARCHITECTURE.md +434 -0
  2. package/dist/cjs/OVERVIEW.md +229 -0
  3. package/dist/cjs/bll/constants.js +86 -0
  4. package/dist/cjs/bll/formatters/booleanFormatter.js +51 -0
  5. package/dist/cjs/bll/formatters/dateFormatter.js +78 -0
  6. package/dist/cjs/bll/formatters/dropdownFormatter.js +34 -0
  7. package/dist/cjs/bll/formatters/factory/index.js +115 -0
  8. package/dist/cjs/bll/formatters/index.js +24 -0
  9. package/dist/cjs/bll/formatters/numberFormatter.js +70 -0
  10. package/dist/cjs/bll/formatters/phoneFormatter.js +57 -0
  11. package/dist/cjs/bll/formatters/regexFormatter.js +52 -0
  12. package/dist/cjs/bll/formatters/ssnFormatter.js +50 -0
  13. package/dist/cjs/bll/formatters/textFormatter.js +43 -0
  14. package/dist/cjs/bll/formatters/zipFormatter.js +48 -0
  15. package/dist/cjs/bll/index.js +62 -0
  16. package/dist/cjs/bll/ssf/index.js +48 -0
  17. package/dist/cjs/bll/ssf/loan.js +81 -0
  18. package/dist/cjs/bll/ssf/loconnect.js +70 -0
  19. package/dist/cjs/bll/ssf/ssfBase.js +97 -0
  20. package/dist/cjs/bll/ssf/types.js +16 -0
  21. package/dist/cjs/bll/types.js +16 -0
  22. package/dist/cjs/bll/validators/dateValidator.js +60 -0
  23. package/dist/cjs/bll/validators/emailValidator.js +47 -0
  24. package/dist/cjs/bll/validators/factory/index.js +81 -0
  25. package/dist/cjs/bll/validators/index.js +24 -0
  26. package/dist/cjs/bll/validators/maxCharValidator.js +49 -0
  27. package/dist/cjs/bll/validators/requiredValidator.js +44 -0
  28. package/dist/cjs/bll/validators/zipValidator.js +53 -0
  29. package/dist/cjs/core/index.js +52 -0
  30. package/dist/cjs/demo/config.js +391 -0
  31. package/dist/cjs/demo/index.js +31 -0
  32. package/dist/cjs/package.json +7 -0
  33. package/dist/cjs/renderer/FieldRenderer.js +45 -0
  34. package/dist/cjs/renderer/base/hooks/fieldDescription.js +39 -0
  35. package/dist/cjs/renderer/base/hooks/fieldDisabled.js +53 -0
  36. package/dist/cjs/renderer/base/hooks/fieldGoTo.js +50 -0
  37. package/dist/cjs/renderer/base/hooks/fieldLocked.js +42 -0
  38. package/dist/cjs/renderer/base/hooks/fieldMeta.js +150 -0
  39. package/dist/cjs/renderer/base/hooks/fieldSubscribers.js +66 -0
  40. package/dist/cjs/renderer/base/hooks/fieldValidation.js +45 -0
  41. package/dist/cjs/renderer/base/hooks/fieldValue.js +215 -0
  42. package/dist/cjs/renderer/base/hooks/hookBase.js +29 -0
  43. package/dist/cjs/renderer/base/hooks/index.js +139 -0
  44. package/dist/cjs/renderer/base/renderer.js +198 -0
  45. package/dist/cjs/renderer/base/rendererValidator.js +97 -0
  46. package/dist/cjs/renderer/factory/index.js +58 -0
  47. package/dist/cjs/renderer/field-renderers/AddonRenderer.js +75 -0
  48. package/dist/cjs/renderer/field-renderers/CheckboxRenderer.js +123 -0
  49. package/dist/cjs/renderer/field-renderers/DateRenderer.js +206 -0
  50. package/dist/cjs/renderer/field-renderers/DropdownRenderer/hook.js +99 -0
  51. package/dist/cjs/renderer/field-renderers/DropdownRenderer/index.js +216 -0
  52. package/dist/cjs/renderer/field-renderers/LargeTextRenderer.js +209 -0
  53. package/dist/cjs/renderer/field-renderers/NumberRenderer.js +216 -0
  54. package/dist/cjs/renderer/field-renderers/RadioGroupRenderer.js +128 -0
  55. package/dist/cjs/renderer/field-renderers/RadioRenderer.js +121 -0
  56. package/dist/cjs/renderer/field-renderers/TextRenderer.js +223 -0
  57. package/dist/cjs/renderer/field-renderers/ToggleRenderer.js +121 -0
  58. package/dist/cjs/renderer/field-renderers/ZipCodeRenderer/helper.js +132 -0
  59. package/dist/cjs/renderer/field-renderers/ZipCodeRenderer/hook.js +128 -0
  60. package/dist/cjs/renderer/field-renderers/ZipCodeRenderer/index.js +273 -0
  61. package/dist/cjs/renderer/index.js +24 -0
  62. package/dist/cjs/renderer/styles.js +51 -0
  63. package/dist/cjs/renderer/types.js +16 -0
  64. package/dist/cjs/tests/base/flowBase.js +125 -0
  65. package/dist/cjs/tests/base/index.js +52 -0
  66. package/dist/cjs/tests/flows/checkboxRendererFlows.js +85 -0
  67. package/dist/cjs/tests/flows/dateRendererFlows.js +870 -0
  68. package/dist/cjs/tests/flows/dropdownRendererFlows.js +591 -0
  69. package/dist/cjs/tests/flows/largeTextRendererFlows.js +99 -0
  70. package/dist/cjs/tests/flows/numberRendererFlows.js +175 -0
  71. package/dist/cjs/tests/flows/radioRendererFlows.js +115 -0
  72. package/dist/cjs/tests/flows/textRendererFlows.js +349 -0
  73. package/dist/cjs/tests/flows/toggleRendererFlows.js +106 -0
  74. package/dist/cjs/tests/flows/zipCodeRendererFlows.js +1163 -0
  75. package/dist/cjs/utils/dateHelper.js +65 -0
  76. package/dist/esm/ARCHITECTURE.md +434 -0
  77. package/dist/esm/OVERVIEW.md +229 -0
  78. package/dist/esm/bll/constants.js +66 -0
  79. package/dist/esm/bll/formatters/booleanFormatter.js +33 -0
  80. package/dist/esm/bll/formatters/dateFormatter.js +48 -0
  81. package/dist/esm/bll/formatters/dropdownFormatter.js +14 -0
  82. package/dist/esm/bll/formatters/factory/index.js +97 -0
  83. package/dist/esm/bll/formatters/index.js +4 -0
  84. package/dist/esm/bll/formatters/numberFormatter.js +54 -0
  85. package/dist/esm/bll/formatters/phoneFormatter.js +41 -0
  86. package/dist/esm/bll/formatters/regexFormatter.js +34 -0
  87. package/dist/esm/bll/formatters/ssnFormatter.js +32 -0
  88. package/dist/esm/bll/formatters/textFormatter.js +25 -0
  89. package/dist/esm/bll/formatters/zipFormatter.js +30 -0
  90. package/dist/esm/bll/index.js +44 -0
  91. package/dist/esm/bll/ssf/index.js +30 -0
  92. package/dist/esm/bll/ssf/loan.js +63 -0
  93. package/dist/esm/bll/ssf/loconnect.js +52 -0
  94. package/dist/esm/bll/ssf/ssfBase.js +67 -0
  95. package/dist/esm/bll/ssf/types.js +0 -0
  96. package/dist/esm/bll/types.js +0 -0
  97. package/dist/esm/bll/validators/dateValidator.js +30 -0
  98. package/dist/esm/bll/validators/emailValidator.js +29 -0
  99. package/dist/esm/bll/validators/factory/index.js +63 -0
  100. package/dist/esm/bll/validators/index.js +4 -0
  101. package/dist/esm/bll/validators/maxCharValidator.js +31 -0
  102. package/dist/esm/bll/validators/requiredValidator.js +26 -0
  103. package/dist/esm/bll/validators/zipValidator.js +35 -0
  104. package/dist/esm/core/index.js +34 -0
  105. package/dist/esm/demo/config.js +371 -0
  106. package/dist/esm/demo/index.js +11 -0
  107. package/dist/esm/package.json +7 -0
  108. package/dist/esm/renderer/FieldRenderer.js +15 -0
  109. package/dist/esm/renderer/base/hooks/fieldDescription.js +19 -0
  110. package/dist/esm/renderer/base/hooks/fieldDisabled.js +33 -0
  111. package/dist/esm/renderer/base/hooks/fieldGoTo.js +30 -0
  112. package/dist/esm/renderer/base/hooks/fieldLocked.js +22 -0
  113. package/dist/esm/renderer/base/hooks/fieldMeta.js +132 -0
  114. package/dist/esm/renderer/base/hooks/fieldSubscribers.js +36 -0
  115. package/dist/esm/renderer/base/hooks/fieldValidation.js +25 -0
  116. package/dist/esm/renderer/base/hooks/fieldValue.js +195 -0
  117. package/dist/esm/renderer/base/hooks/hookBase.js +9 -0
  118. package/dist/esm/renderer/base/hooks/index.js +121 -0
  119. package/dist/esm/renderer/base/renderer.js +178 -0
  120. package/dist/esm/renderer/base/rendererValidator.js +77 -0
  121. package/dist/esm/renderer/factory/index.js +38 -0
  122. package/dist/esm/renderer/field-renderers/AddonRenderer.js +55 -0
  123. package/dist/esm/renderer/field-renderers/CheckboxRenderer.js +93 -0
  124. package/dist/esm/renderer/field-renderers/DateRenderer.js +176 -0
  125. package/dist/esm/renderer/field-renderers/DropdownRenderer/hook.js +79 -0
  126. package/dist/esm/renderer/field-renderers/DropdownRenderer/index.js +186 -0
  127. package/dist/esm/renderer/field-renderers/LargeTextRenderer.js +179 -0
  128. package/dist/esm/renderer/field-renderers/NumberRenderer.js +188 -0
  129. package/dist/esm/renderer/field-renderers/RadioGroupRenderer.js +108 -0
  130. package/dist/esm/renderer/field-renderers/RadioRenderer.js +91 -0
  131. package/dist/esm/renderer/field-renderers/TextRenderer.js +197 -0
  132. package/dist/esm/renderer/field-renderers/ToggleRenderer.js +91 -0
  133. package/dist/esm/renderer/field-renderers/ZipCodeRenderer/helper.js +112 -0
  134. package/dist/esm/renderer/field-renderers/ZipCodeRenderer/hook.js +108 -0
  135. package/dist/esm/renderer/field-renderers/ZipCodeRenderer/index.js +247 -0
  136. package/dist/esm/renderer/index.js +4 -0
  137. package/dist/esm/renderer/styles.js +21 -0
  138. package/dist/esm/renderer/types.js +0 -0
  139. package/dist/esm/tests/base/flowBase.js +105 -0
  140. package/dist/esm/tests/base/index.js +22 -0
  141. package/dist/esm/tests/flows/checkboxRendererFlows.js +65 -0
  142. package/dist/esm/tests/flows/dateRendererFlows.js +850 -0
  143. package/dist/esm/tests/flows/dropdownRendererFlows.js +571 -0
  144. package/dist/esm/tests/flows/largeTextRendererFlows.js +79 -0
  145. package/dist/esm/tests/flows/numberRendererFlows.js +155 -0
  146. package/dist/esm/tests/flows/radioRendererFlows.js +95 -0
  147. package/dist/esm/tests/flows/textRendererFlows.js +329 -0
  148. package/dist/esm/tests/flows/toggleRendererFlows.js +86 -0
  149. package/dist/esm/tests/flows/zipCodeRendererFlows.js +1143 -0
  150. package/dist/esm/utils/dateHelper.js +35 -0
  151. package/dist/types/lib/bll/constants.d.ts +9 -0
  152. package/dist/types/lib/bll/formatters/booleanFormatter.d.ts +5 -0
  153. package/dist/types/lib/bll/formatters/dateFormatter.d.ts +28 -0
  154. package/dist/types/lib/bll/formatters/dropdownFormatter.d.ts +6 -0
  155. package/dist/types/lib/bll/formatters/factory/index.d.ts +71 -0
  156. package/dist/types/lib/bll/formatters/index.d.ts +2 -0
  157. package/dist/types/lib/bll/formatters/numberFormatter.d.ts +6 -0
  158. package/dist/types/lib/bll/formatters/phoneFormatter.d.ts +7 -0
  159. package/dist/types/lib/bll/formatters/regexFormatter.d.ts +5 -0
  160. package/dist/types/lib/bll/formatters/ssnFormatter.d.ts +5 -0
  161. package/dist/types/lib/bll/formatters/textFormatter.d.ts +6 -0
  162. package/dist/types/lib/bll/formatters/zipFormatter.d.ts +5 -0
  163. package/dist/types/lib/bll/index.d.ts +20 -0
  164. package/dist/types/lib/bll/ssf/index.d.ts +25 -0
  165. package/dist/types/lib/bll/ssf/loan.d.ts +16 -0
  166. package/dist/types/lib/bll/ssf/loconnect.d.ts +15 -0
  167. package/dist/types/lib/bll/ssf/ssfBase.d.ts +23 -0
  168. package/dist/types/lib/bll/ssf/types.d.ts +99 -0
  169. package/dist/types/lib/bll/types.d.ts +47 -0
  170. package/dist/types/lib/bll/validators/dateValidator.d.ts +16 -0
  171. package/dist/types/lib/bll/validators/emailValidator.d.ts +4 -0
  172. package/dist/types/lib/bll/validators/factory/index.d.ts +15 -0
  173. package/dist/types/lib/bll/validators/index.d.ts +2 -0
  174. package/dist/types/lib/bll/validators/maxCharValidator.d.ts +4 -0
  175. package/dist/types/lib/bll/validators/requiredValidator.d.ts +4 -0
  176. package/dist/types/lib/bll/validators/zipValidator.d.ts +5 -0
  177. package/dist/types/lib/core/index.d.ts +29 -0
  178. package/dist/types/lib/demo/config.d.ts +11 -0
  179. package/dist/types/lib/demo/index.d.ts +1 -0
  180. package/dist/types/lib/renderer/FieldRenderer.d.ts +5 -0
  181. package/dist/types/lib/renderer/base/hooks/fieldDescription.d.ts +5 -0
  182. package/dist/types/lib/renderer/base/hooks/fieldDisabled.d.ts +10 -0
  183. package/dist/types/lib/renderer/base/hooks/fieldGoTo.d.ts +4 -0
  184. package/dist/types/lib/renderer/base/hooks/fieldLocked.d.ts +4 -0
  185. package/dist/types/lib/renderer/base/hooks/fieldMeta.d.ts +10 -0
  186. package/dist/types/lib/renderer/base/hooks/fieldSubscribers.d.ts +6 -0
  187. package/dist/types/lib/renderer/base/hooks/fieldValidation.d.ts +9 -0
  188. package/dist/types/lib/renderer/base/hooks/fieldValue.d.ts +31 -0
  189. package/dist/types/lib/renderer/base/hooks/hookBase.d.ts +9 -0
  190. package/dist/types/lib/renderer/base/hooks/index.d.ts +19 -0
  191. package/dist/types/lib/renderer/base/renderer.d.ts +43 -0
  192. package/dist/types/lib/renderer/base/rendererValidator.d.ts +15 -0
  193. package/dist/types/lib/renderer/factory/index.d.ts +5 -0
  194. package/dist/types/lib/renderer/field-renderers/AddonRenderer.d.ts +12 -0
  195. package/dist/types/lib/renderer/field-renderers/CheckboxRenderer.d.ts +7 -0
  196. package/dist/types/lib/renderer/field-renderers/DateRenderer.d.ts +13 -0
  197. package/dist/types/lib/renderer/field-renderers/DropdownRenderer/hook.d.ts +23 -0
  198. package/dist/types/lib/renderer/field-renderers/DropdownRenderer/index.d.ts +12 -0
  199. package/dist/types/lib/renderer/field-renderers/LargeTextRenderer.d.ts +17 -0
  200. package/dist/types/lib/renderer/field-renderers/NumberRenderer.d.ts +12 -0
  201. package/dist/types/lib/renderer/field-renderers/RadioGroupRenderer.d.ts +8 -0
  202. package/dist/types/lib/renderer/field-renderers/RadioRenderer.d.ts +8 -0
  203. package/dist/types/lib/renderer/field-renderers/TextRenderer.d.ts +8 -0
  204. package/dist/types/lib/renderer/field-renderers/ToggleRenderer.d.ts +24 -0
  205. package/dist/types/lib/renderer/field-renderers/ZipCodeRenderer/helper.d.ts +48 -0
  206. package/dist/types/lib/renderer/field-renderers/ZipCodeRenderer/hook.d.ts +17 -0
  207. package/dist/types/lib/renderer/field-renderers/ZipCodeRenderer/index.d.ts +9 -0
  208. package/dist/types/lib/renderer/index.d.ts +2 -0
  209. package/dist/types/lib/renderer/styles.d.ts +7 -0
  210. package/dist/types/lib/renderer/types.d.ts +325 -0
  211. package/dist/types/lib/tests/base/flowBase.d.ts +13 -0
  212. package/dist/types/lib/tests/base/index.d.ts +6 -0
  213. package/dist/types/lib/tests/flows/checkboxRendererFlows.d.ts +9 -0
  214. package/dist/types/lib/tests/flows/dateRendererFlows.d.ts +120 -0
  215. package/dist/types/lib/tests/flows/dropdownRendererFlows.d.ts +92 -0
  216. package/dist/types/lib/tests/flows/largeTextRendererFlows.d.ts +9 -0
  217. package/dist/types/lib/tests/flows/numberRendererFlows.d.ts +11 -0
  218. package/dist/types/lib/tests/flows/radioRendererFlows.d.ts +10 -0
  219. package/dist/types/lib/tests/flows/textRendererFlows.d.ts +16 -0
  220. package/dist/types/lib/tests/flows/toggleRendererFlows.d.ts +10 -0
  221. package/dist/types/lib/tests/flows/zipCodeRendererFlows.d.ts +169 -0
  222. package/dist/types/lib/tests/loan-field-renderer-flows.test.d.ts +1 -0
  223. package/dist/types/lib/utils/dateHelper.d.ts +8 -0
  224. package/dist/types/tsconfig.tsbuildinfo +1 -0
  225. package/package.json +103 -0
@@ -0,0 +1,112 @@
1
+ import { useCallback, useEffect, useRef } from "react";
2
+ const useDebouncedCallback = (callback, waitTimeout = 150, options = { leading: false }) => {
3
+ const timeoutRef = useRef(null);
4
+ const leadingRef = useRef(options.leading ?? false);
5
+ const callbackRef = useRef(callback);
6
+ useEffect(() => {
7
+ callbackRef.current = callback;
8
+ }, [callback]);
9
+ const clearTimeoutFn = useCallback(() => {
10
+ if (timeoutRef.current !== null) {
11
+ window.clearTimeout(timeoutRef.current);
12
+ timeoutRef.current = null;
13
+ }
14
+ }, []);
15
+ useEffect(() => clearTimeoutFn, [clearTimeoutFn]);
16
+ const debouncedCallback = useCallback(
17
+ (...args) => {
18
+ clearTimeoutFn();
19
+ if (leadingRef.current) {
20
+ callbackRef.current(...args);
21
+ leadingRef.current = false;
22
+ } else {
23
+ timeoutRef.current = window.setTimeout(() => {
24
+ callbackRef.current(...args);
25
+ leadingRef.current = options.leading ?? false;
26
+ }, waitTimeout);
27
+ }
28
+ },
29
+ [waitTimeout, clearTimeoutFn, options.leading]
30
+ );
31
+ return debouncedCallback;
32
+ };
33
+ class ZipCodeHelper {
34
+ // Prevent instantiation - this is a static utility class
35
+ constructor() {
36
+ }
37
+ static SECTION_OPTION = {
38
+ dsId: "section",
39
+ type: "section",
40
+ label: "Please select the appropriate city",
41
+ options: [],
42
+ zipNumber: "",
43
+ cityData: null
44
+ };
45
+ static DEFAULT_COUNTRY_CODE = "US";
46
+ /**
47
+ * Normalizes city data from various API response formats
48
+ * Handles both uppercase and lowercase property names
49
+ * @param cityData - The city data object to normalize
50
+ * @returns Normalized object with lowercase property names
51
+ */
52
+ static normalizeCityData(cityData) {
53
+ return {
54
+ city: cityData?.City ?? cityData?.city ?? "",
55
+ state: cityData?.State ?? cityData?.state ?? "",
56
+ county: cityData?.County ?? cityData?.county ?? ""
57
+ };
58
+ }
59
+ /**
60
+ * Formats a city data option into a readable label
61
+ * Format: "City - County - State"
62
+ * @param option - The city data to format
63
+ * @returns Formatted string label
64
+ */
65
+ static formatOptionLabel(option) {
66
+ return [
67
+ option.City ?? option.city ?? "",
68
+ option.County ?? option.county ?? "",
69
+ option.State ?? option.state ?? ""
70
+ ].filter(Boolean).join(" - ");
71
+ }
72
+ /**
73
+ * Builds autocomplete options from zip code lookup data
74
+ * When multiple cities exist for a zip code, adds a section header
75
+ * @param zip - The zip code string
76
+ * @param data - Array of city data for the zip code
77
+ * @returns Array of autocomplete options
78
+ */
79
+ static buildOptions(zip, data) {
80
+ const optionEntries = data.map((option, index) => ({
81
+ dsId: `${index}`,
82
+ type: "option",
83
+ label: this.formatOptionLabel(option),
84
+ value: zip,
85
+ zipNumber: zip,
86
+ cityData: option
87
+ }));
88
+ if (optionEntries.length <= 1) {
89
+ return optionEntries;
90
+ }
91
+ return [
92
+ { ...this.SECTION_OPTION, zipNumber: zip, options: optionEntries },
93
+ ...optionEntries
94
+ ];
95
+ }
96
+ /**
97
+ * Validates whether any geo fields contain non-empty address data
98
+ * @param geoFields - Optional geo fields object to validate
99
+ * @returns True if any geo field has non-empty string value
100
+ */
101
+ static hasAddressTargets(geoFields) {
102
+ return Boolean(
103
+ geoFields && Object.values(geoFields).some(
104
+ (value) => typeof value === "string" && value.length
105
+ )
106
+ );
107
+ }
108
+ }
109
+ export {
110
+ ZipCodeHelper,
111
+ useDebouncedCallback
112
+ };
@@ -0,0 +1,108 @@
1
+ import { useCallback, useMemo, useState } from "react";
2
+ import { ZipCodeHelper, useDebouncedCallback } from "./helper";
3
+ const useZipCodeLookup = ({
4
+ ssf,
5
+ onGeoLookupComplete,
6
+ useLatLong,
7
+ isForeignAddress,
8
+ geoFields
9
+ }) => {
10
+ const [options, setOptions] = useState([]);
11
+ const shouldAutoFill = useMemo(
12
+ () => ZipCodeHelper.hasAddressTargets(geoFields),
13
+ [geoFields]
14
+ );
15
+ const applyAddressData = useCallback(
16
+ async (zipNumber, cityData) => {
17
+ if (!cityData) return;
18
+ onGeoLookupComplete?.(zipNumber, cityData);
19
+ if (!shouldAutoFill || !geoFields) return;
20
+ const { city, state, county, country, zip } = geoFields;
21
+ const normalized = ZipCodeHelper.normalizeCityData(cityData);
22
+ const payload = {};
23
+ const fieldMappings = [
24
+ [zip, zipNumber],
25
+ [city, normalized.city],
26
+ [state, normalized.state],
27
+ [county, normalized.county]
28
+ ];
29
+ fieldMappings.forEach(([fieldKey, value]) => {
30
+ if (fieldKey && value) {
31
+ payload[fieldKey] = value;
32
+ }
33
+ });
34
+ if (country && !isForeignAddress) {
35
+ payload[country] = ZipCodeHelper.DEFAULT_COUNTRY_CODE;
36
+ }
37
+ if (Object.keys(payload).length) {
38
+ try {
39
+ await ssf.setFieldsValues(payload, true);
40
+ } catch (error) {
41
+ }
42
+ }
43
+ },
44
+ [geoFields, isForeignAddress, shouldAutoFill, ssf, onGeoLookupComplete]
45
+ );
46
+ const fetchZipResults = useCallback(
47
+ async (zip) => {
48
+ const rawResults = useLatLong ? await ssf.getZipCodeOptionsWithLatLong(zip) : await ssf.getZipCodeOptions(zip);
49
+ return Array.isArray(rawResults) ? rawResults : [];
50
+ },
51
+ [ssf, useLatLong]
52
+ );
53
+ const handleLookupResults = useCallback(
54
+ async (zip, results) => {
55
+ if (results.length === 1) {
56
+ setOptions([]);
57
+ await applyAddressData(zip, results[0]);
58
+ } else if (results.length > 1) {
59
+ setOptions(ZipCodeHelper.buildOptions(zip, results));
60
+ } else {
61
+ setOptions([]);
62
+ }
63
+ },
64
+ [applyAddressData]
65
+ );
66
+ const runLookup = useCallback(
67
+ async (zip) => {
68
+ if (!zip || isForeignAddress) {
69
+ setOptions([]);
70
+ return;
71
+ }
72
+ try {
73
+ const results = await fetchZipResults(zip);
74
+ await handleLookupResults(zip, results);
75
+ } catch (error) {
76
+ setOptions([]);
77
+ }
78
+ },
79
+ [fetchZipResults, handleLookupResults, isForeignAddress]
80
+ );
81
+ const clearOptions = useCallback(() => setOptions([]), []);
82
+ const debouncedLookup = useDebouncedCallback((zip) => {
83
+ runLookup(zip);
84
+ });
85
+ const handleOptionSelect = useCallback(
86
+ (label) => {
87
+ if (!label) return null;
88
+ const match = options.find(
89
+ (item) => item.type === "option" && item.label === label
90
+ );
91
+ if (!match) return null;
92
+ clearOptions();
93
+ applyAddressData(match.zipNumber, match.cityData).catch(() => {
94
+ });
95
+ return match;
96
+ },
97
+ [applyAddressData, clearOptions, options]
98
+ );
99
+ return {
100
+ options,
101
+ searchZipCode: debouncedLookup,
102
+ handleOptionSelect,
103
+ clearOptions
104
+ };
105
+ };
106
+ export {
107
+ useZipCodeLookup
108
+ };
@@ -0,0 +1,247 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import React, { useCallback } from "react";
3
+ import {
4
+ useZipCodeMask,
5
+ DSAutocomplete,
6
+ DSInputText
7
+ } from "@elliemae/ds-controlled-form";
8
+ import { Renderer } from "../../base/renderer";
9
+ import { AddonRenderer } from "../AddonRenderer";
10
+ import { useZipCodeLookup } from "./hook";
11
+ class ZipCodeRenderer extends Renderer {
12
+ AddOnRenderer;
13
+ constructor() {
14
+ super();
15
+ this.AddOnRenderer = new AddonRenderer().render();
16
+ this.BRWrapper = this.renderBR();
17
+ }
18
+ render() {
19
+ const ZipComponent = (props) => {
20
+ const {
21
+ id,
22
+ fieldId,
23
+ isDisable,
24
+ validationRules = [],
25
+ onChange,
26
+ onBlur,
27
+ label,
28
+ rightAddon,
29
+ isForeignAddress = false,
30
+ useLatLong = false,
31
+ onGeoLookupComplete,
32
+ geoFields,
33
+ brCompProps,
34
+ ...rest
35
+ } = props;
36
+ const { BRWrapper, AddOnRenderer } = this;
37
+ const {
38
+ fieldMetadata: {
39
+ isLockField = false,
40
+ maxLength = 0,
41
+ fieldFormat = ""
42
+ } = {},
43
+ fieldValue,
44
+ setFieldValue,
45
+ isLockedState,
46
+ isSearchedField,
47
+ setIsSearchedField,
48
+ isFieldDisabled,
49
+ validationResult,
50
+ ariaDescProps
51
+ } = this.initVM(props);
52
+ this.fieldFormat = fieldFormat;
53
+ const { options, searchZipCode, handleOptionSelect, clearOptions } = useZipCodeLookup({
54
+ ssf: this.BLL.SSF,
55
+ onGeoLookupComplete,
56
+ useLatLong,
57
+ isForeignAddress,
58
+ geoFields: { ...geoFields, zip: fieldId }
59
+ });
60
+ const zipCodeInputProps = useZipCodeMask({
61
+ valueSetter: (value) => {
62
+ if (!isDisable && !isFieldDisabled && !isForeignAddress) {
63
+ setFieldValue(value);
64
+ }
65
+ }
66
+ });
67
+ const handleForeignAddressChange = useCallback(
68
+ (newValue) => {
69
+ setFieldValue(newValue);
70
+ if (onChange) {
71
+ onChange(fieldId, newValue);
72
+ }
73
+ },
74
+ [fieldId, onChange, setFieldValue]
75
+ );
76
+ const handleUSZipChange = useCallback(
77
+ (newValue) => {
78
+ if (!newValue) {
79
+ setFieldValue("");
80
+ clearOptions();
81
+ if (onChange) {
82
+ onChange(fieldId, "");
83
+ }
84
+ return;
85
+ }
86
+ if (!/\d/.test(newValue)) {
87
+ return;
88
+ }
89
+ const formattedValue = this.getFormattedValue(newValue);
90
+ setFieldValue(formattedValue);
91
+ const parsedValue = this.getParsedValue(formattedValue);
92
+ if (onChange) {
93
+ onChange(fieldId, parsedValue);
94
+ }
95
+ const { isValid } = this.BLL.Validator.createValidator("zip").validate(formattedValue);
96
+ if (isValid) {
97
+ searchZipCode(formattedValue);
98
+ } else {
99
+ clearOptions();
100
+ }
101
+ },
102
+ [fieldId, onChange, setFieldValue, clearOptions, searchZipCode]
103
+ );
104
+ const onChangeEvent = useCallback(
105
+ (newValue) => {
106
+ setIsSearchedField(false);
107
+ if (isDisable || isFieldDisabled) return;
108
+ if (isForeignAddress) {
109
+ handleForeignAddressChange(newValue);
110
+ } else {
111
+ handleUSZipChange(newValue);
112
+ }
113
+ },
114
+ [
115
+ isForeignAddress,
116
+ isDisable,
117
+ isFieldDisabled,
118
+ setIsSearchedField,
119
+ handleForeignAddressChange,
120
+ handleUSZipChange
121
+ ]
122
+ );
123
+ const onAutocompleteSelect = useCallback(
124
+ (selectedLabel) => {
125
+ if (isForeignAddress || isDisable || isFieldDisabled) return;
126
+ const selectedOption = handleOptionSelect(selectedLabel);
127
+ if (!selectedOption) return;
128
+ const formattedValue = this.getFormattedValue(
129
+ selectedOption.zipNumber
130
+ );
131
+ setFieldValue(formattedValue);
132
+ const parsedValue = this.getParsedValue(formattedValue);
133
+ if (onChange) {
134
+ onChange(fieldId, parsedValue);
135
+ }
136
+ },
137
+ [
138
+ fieldId,
139
+ handleOptionSelect,
140
+ isForeignAddress,
141
+ isDisable,
142
+ isFieldDisabled,
143
+ onChange,
144
+ setFieldValue
145
+ ]
146
+ );
147
+ const onBlurEvent = useCallback(
148
+ (e) => {
149
+ const { value } = e.target;
150
+ if (isDisable || isFieldDisabled) return;
151
+ const formattedValue = isForeignAddress ? value : this.getFormattedValue(value);
152
+ setFieldValue(formattedValue);
153
+ const rulesToCheck = [...validationRules];
154
+ const tempValResult = this.Hooks.runValidationsOnValue(
155
+ formattedValue,
156
+ rulesToCheck
157
+ );
158
+ this.VM.setValidationResult(tempValResult);
159
+ if (!tempValResult.isValid) {
160
+ return;
161
+ }
162
+ const parsedValue = isForeignAddress ? formattedValue : this.getParsedValue(formattedValue);
163
+ (async () => {
164
+ await this.BLL.SSF.setFieldValue(fieldId, parsedValue);
165
+ })();
166
+ if (onBlur) {
167
+ onBlur(fieldId, parsedValue);
168
+ }
169
+ },
170
+ // eslint-disable-next-line react-hooks/exhaustive-deps
171
+ [fieldId, isForeignAddress, isDisable, onBlur, validationRules]
172
+ );
173
+ const validationProps = this.buildValidationProps(validationResult);
174
+ return /* @__PURE__ */ jsx(
175
+ BRWrapper,
176
+ {
177
+ fieldId,
178
+ inputID: fieldId,
179
+ label,
180
+ value: fieldValue || "",
181
+ ...validationProps,
182
+ ...brCompProps || {},
183
+ withHighlight: isSearchedField,
184
+ children: /* @__PURE__ */ jsx(
185
+ AddOnRenderer,
186
+ {
187
+ rendererType: "addon",
188
+ fieldId,
189
+ isLocked: isLockedState,
190
+ isPadLock: isLockField,
191
+ isDisable,
192
+ rightAddon,
193
+ onPadLockToggle: () => this.toggleLockStatus(fieldId, isLockedState || false),
194
+ children: isForeignAddress ? /* @__PURE__ */ jsx(
195
+ DSInputText,
196
+ {
197
+ id,
198
+ "data-testid": fieldId,
199
+ value: fieldValue || "",
200
+ onValueChange: onChangeEvent,
201
+ onBlur: onBlurEvent,
202
+ disabled: isFieldDisabled || isDisable,
203
+ applyAriaDisabled: isFieldDisabled || isDisable,
204
+ ...maxLength && maxLength > 0 && { maxLength },
205
+ ...ariaDescProps,
206
+ ...this.sanitize(rest)
207
+ }
208
+ ) : /* @__PURE__ */ jsx(
209
+ DSAutocomplete,
210
+ {
211
+ placeholder: "",
212
+ options,
213
+ filter: fieldValue || "",
214
+ onSelect: onAutocompleteSelect,
215
+ disabled: isFieldDisabled || isDisable,
216
+ children: /* @__PURE__ */ jsx(
217
+ DSInputText,
218
+ {
219
+ id,
220
+ "data-testid": fieldId,
221
+ value: fieldValue || "",
222
+ autoComplete: "off",
223
+ onValueChange: onChangeEvent,
224
+ onBlur: onBlurEvent,
225
+ disabled: isFieldDisabled || isDisable,
226
+ applyAriaDisabled: isFieldDisabled || isDisable,
227
+ ...maxLength && maxLength > 0 && { maxLength },
228
+ ...zipCodeInputProps,
229
+ ...ariaDescProps,
230
+ ...this.sanitize(rest)
231
+ }
232
+ )
233
+ }
234
+ )
235
+ }
236
+ )
237
+ }
238
+ );
239
+ };
240
+ return React.memo(ZipComponent);
241
+ }
242
+ }
243
+ var ZipCodeRenderer_default = ZipCodeRenderer;
244
+ export {
245
+ ZipCodeRenderer,
246
+ ZipCodeRenderer_default as default
247
+ };
@@ -0,0 +1,4 @@
1
+ import { FieldRenderer } from "./FieldRenderer";
2
+ export {
3
+ FieldRenderer
4
+ };
@@ -0,0 +1,21 @@
1
+ import styled from "styled-components";
2
+ import { getDefaultTheme } from "@elliemae/pui-theme";
3
+ import { DSControlledDateTimePicker } from "@elliemae/ds-form-date-time-picker";
4
+ const EmptyLabel = styled.label`
5
+ display: block;
6
+ height: 1rem;
7
+ line-height: 1.15rem;
8
+ `;
9
+ const StyledDateTimePicker = styled(DSControlledDateTimePicker)`
10
+ width: auto;
11
+
12
+ & div:last-child {
13
+ justify-content: end;
14
+ }
15
+ `;
16
+ const theme = getDefaultTheme();
17
+ export {
18
+ EmptyLabel,
19
+ StyledDateTimePicker,
20
+ theme
21
+ };
File without changes
@@ -0,0 +1,105 @@
1
+ class FlowBase {
2
+ constructor(flowParams) {
3
+ this.flowParams = flowParams;
4
+ }
5
+ flowParams;
6
+ setTimeoutAwaitable(timeout = 100) {
7
+ return new Promise((resolve) => {
8
+ setTimeout(() => {
9
+ resolve();
10
+ }, timeout);
11
+ });
12
+ }
13
+ getFlowName(rendererName, description) {
14
+ return `component: ${rendererName} - ${description}`;
15
+ }
16
+ static get Mocks() {
17
+ return {
18
+ apis: [],
19
+ ssf: {
20
+ loconnect: {
21
+ getStateDropDownList: [
22
+ { name: "Alabama", value: "AL" },
23
+ { name: "California", value: "CA" },
24
+ { name: "New York", value: "NY" },
25
+ { name: "Texas", value: "TX" }
26
+ ],
27
+ // Zip code lookup - single result (auto-fill)
28
+ getZipCodeData: (zip) => {
29
+ if (zip === "90210") {
30
+ return [
31
+ { City: "Beverly Hills", State: "CA", County: "Los Angeles" }
32
+ ];
33
+ }
34
+ if (zip === "10001") {
35
+ return [
36
+ { City: "New York", State: "NY", County: "New York" },
37
+ { City: "Manhattan", State: "NY", County: "New York" }
38
+ ];
39
+ }
40
+ if (zip === "94105") {
41
+ return [
42
+ {
43
+ City: "San Francisco",
44
+ State: "CA",
45
+ County: "San Francisco"
46
+ }
47
+ ];
48
+ }
49
+ if (zip === "75001") {
50
+ return [{ City: "Addison", State: "TX", County: "Dallas" }];
51
+ }
52
+ return [];
53
+ },
54
+ // Zip code lookup with lat/long
55
+ getZipCodeLatLong: (zip) => {
56
+ if (zip === "90210") {
57
+ return [
58
+ {
59
+ city: "Beverly Hills",
60
+ state: "CA",
61
+ county: "Los Angeles",
62
+ latitude: "34.0901",
63
+ longitude: "-118.4065"
64
+ }
65
+ ];
66
+ }
67
+ if (zip === "10001") {
68
+ return [
69
+ {
70
+ city: "New York",
71
+ state: "NY",
72
+ county: "New York",
73
+ latitude: "40.7484",
74
+ longitude: "-73.9967"
75
+ },
76
+ {
77
+ city: "Manhattan",
78
+ state: "NY",
79
+ county: "New York",
80
+ latitude: "40.7831",
81
+ longitude: "-73.9712"
82
+ }
83
+ ];
84
+ }
85
+ if (zip === "94105") {
86
+ return [
87
+ {
88
+ city: "San Francisco",
89
+ state: "CA",
90
+ county: "San Francisco",
91
+ latitude: "37.7893",
92
+ longitude: "-122.3959"
93
+ }
94
+ ];
95
+ }
96
+ return [];
97
+ }
98
+ }
99
+ }
100
+ };
101
+ }
102
+ }
103
+ export {
104
+ FlowBase
105
+ };
@@ -0,0 +1,22 @@
1
+ import React from "react";
2
+ import { render } from "@testing-library/react";
3
+ import { ThemeProvider } from "styled-components";
4
+ import { getDefaultTheme } from "@elliemae/pui-theme";
5
+ import { FlowRunner } from "@elliemae/flow-driven-testing";
6
+ import { Demo } from "../../demo";
7
+ class TestBase extends FlowRunner {
8
+ mountApp() {
9
+ return render(
10
+ React.createElement(
11
+ ThemeProvider,
12
+ { theme: getDefaultTheme() },
13
+ React.createElement(Demo)
14
+ )
15
+ );
16
+ }
17
+ init() {
18
+ }
19
+ }
20
+ export {
21
+ TestBase
22
+ };
@@ -0,0 +1,65 @@
1
+ import { TTriggerActions } from "@elliemae/flow-driven-testing";
2
+ import { FlowBase } from "../base/flowBase";
3
+ class CheckboxRendererFlows extends FlowBase {
4
+ constructor(flowParams) {
5
+ super(flowParams);
6
+ this.flowParams = flowParams;
7
+ }
8
+ flowParams;
9
+ UpdateCheckboxStatus() {
10
+ return {
11
+ [`${this.flowParams.inboundFlow}`]: {
12
+ name: this.getFlowName("CheckboxRenderer", "update checkbox status"),
13
+ startFlow: [
14
+ {
15
+ selector: '[data-testid="LE1.X78"]',
16
+ action: TTriggerActions.CLICK
17
+ }
18
+ ],
19
+ asserts: [
20
+ {
21
+ ssfFunctionsCalled: ["setFields"]
22
+ }
23
+ ],
24
+ transitions: [
25
+ {
26
+ to: "checkCheckboxDisabled"
27
+ }
28
+ ]
29
+ }
30
+ };
31
+ }
32
+ CheckCheckboxDisabled() {
33
+ return {
34
+ checkCheckboxDisabled: {
35
+ name: this.getFlowName("CheckboxRenderer", "check checkbox disabled"),
36
+ startFlow: [
37
+ {
38
+ selector: '[data-testid="LE1.X78"]',
39
+ action: TTriggerActions.CLICK
40
+ }
41
+ ],
42
+ asserts: [
43
+ {
44
+ selector: '[data-testid="LE1.X79"]',
45
+ isEnabled: false
46
+ }
47
+ ],
48
+ transitions: [
49
+ {
50
+ to: this.flowParams.outboundFlow || ""
51
+ }
52
+ ]
53
+ }
54
+ };
55
+ }
56
+ get Flows() {
57
+ return {
58
+ ...this.UpdateCheckboxStatus(),
59
+ ...this.CheckCheckboxDisabled()
60
+ };
61
+ }
62
+ }
63
+ export {
64
+ CheckboxRendererFlows
65
+ };