@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,65 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var dateHelper_exports = {};
30
+ __export(dateHelper_exports, {
31
+ calNextBusinessDay: () => calNextBusinessDay,
32
+ calNextPresumedRecievedDay: () => calNextPresumedRecievedDay,
33
+ convertToLocalDateTime: () => convertToLocalDateTime,
34
+ formatDateForBr: () => formatDateForBr,
35
+ formatISODate: () => formatISODate,
36
+ isHoliday: () => isHoliday,
37
+ isSunday: () => isSunday
38
+ });
39
+ module.exports = __toCommonJS(dateHelper_exports);
40
+ var import_moment = __toESM(require("moment"));
41
+ const isSunday = (momentDateObj) => (0, import_moment.default)(momentDateObj).format("dddd") === "Sunday";
42
+ const isHoliday = (momentDateObj, holidays) => holidays.includes((0, import_moment.default)(momentDateObj).format("YYYY-MM-DD"));
43
+ const calNextBusinessDay = (dayCound, momentDateObj, holidays) => {
44
+ const nextDateObj = momentDateObj.add(dayCound, "d");
45
+ if (isSunday(nextDateObj) || isHoliday(nextDateObj, holidays)) {
46
+ calNextBusinessDay(dayCound, nextDateObj, holidays);
47
+ }
48
+ return nextDateObj;
49
+ };
50
+ const calNextPresumedRecievedDay = (dayCount, momentDateObj, holidays) => {
51
+ const nextPresumedRecievedDay = isSunday(momentDateObj) || isHoliday(momentDateObj, holidays) ? calNextBusinessDay(1, momentDateObj, holidays) : momentDateObj;
52
+ for (let index = 0; index < dayCount; index += 1) {
53
+ calNextBusinessDay(1, nextPresumedRecievedDay, holidays);
54
+ }
55
+ return nextPresumedRecievedDay;
56
+ };
57
+ const formatISODate = (date) => (0, import_moment.default)(date).utc().format("MM/DD/YYYY");
58
+ const formatDateForBr = (date, format) => {
59
+ if (!date) return date;
60
+ if (format === "ISO") {
61
+ return (0, import_moment.default)(date).format("YYYY-MM-DD");
62
+ }
63
+ return (0, import_moment.default)(date).format("MM/DD/YYYY");
64
+ };
65
+ const convertToLocalDateTime = (date) => date ? import_moment.default.utc(date).local().format("MM/DD/YYYY [at] hh:mm A") : date;
@@ -0,0 +1,434 @@
1
+ # Loan Field Renderers — Technical Documentation
2
+
3
+ ---
4
+
5
+ ## 1. Purpose
6
+
7
+ **Loan Field Renderers** is a metadata-driven smart control system that renders loan form fields as UI components. Instead of every page manually wiring up data fetching, formatting, validation, locking, accessibility, and persistence for each field, this system encapsulates all of that behind a single declarative `<FieldRenderer>` component.
8
+
9
+ A consuming page only needs to specify:
10
+
11
+ - **`fieldId`** — the loan field identifier (e.g., `"FR0104"`)
12
+ - **`rendererType`** — the desired UI control (e.g., `"text"`, `"number"`, `"dropdown"`)
13
+
14
+ The system then automatically:
15
+
16
+ - Fetches field metadata (format, options, read-only, lock, maxLength, description) from SSF
17
+ - Fetches and formats the current field value for display
18
+ - Applies the correct input mask (Phone, SSN, number, date, zip)
19
+ - Validates user input on blur using configurable rules
20
+ - Parses display values back to raw data and persists to the loan object
21
+ - Manages lock/unlock, disabled, and read-only states
22
+ - Provides Go-To-Field navigation highlighting
23
+ - Builds ARIA accessibility attributes from field descriptions
24
+ - Subscribes to loan change events for live refresh
25
+
26
+ ---
27
+
28
+ ## 2. Architecture & Layers
29
+
30
+ The system follows a **layered architecture** with clear separation of concerns. Each layer has a single responsibility and communicates downward through well-defined interfaces.
31
+
32
+ ```
33
+ ┌──────────────────────────────────────────────────────────────┐
34
+ │ CONSUMER / PAGE LAYER │
35
+ │ <FieldRenderer fieldId="FR0104" rendererType="number" /> │
36
+ └──────────────────────────────────┬───────────────────────────┘
37
+
38
+
39
+ ┌──────────────────────────────────────────────────────────────┐
40
+ │ ENTRY POINT LAYER │
41
+ │ FieldRenderer.tsx → RendererFactory.create(rendererType) │
42
+ └──────────────────────────────────┬───────────────────────────┘
43
+
44
+
45
+ ┌──────────────────────────────────────────────────────────────┐
46
+ │ RENDERER LAYER (renderer/) │
47
+ │ │
48
+ │ ┌─────────────────────┐ ┌────────────────────────────────┐ │
49
+ │ │ Specific Renderers │ │ Base Infrastructure │ │
50
+ │ │ ───────────────── │ │ ────────────────── │ │
51
+ │ │ TextRenderer │ │ Renderer<T> (abstract base) │ │
52
+ │ │ NumberRenderer │ │ RendererValidator │ │
53
+ │ │ DateRenderer │ │ Hooks (Mixin-composed) │ │
54
+ │ │ DropdownRenderer │ │ ├ FieldMeta │ │
55
+ │ │ ZipCodeRenderer │ │ ├ FieldValue │ │
56
+ │ │ CheckboxRenderer │ │ ├ FieldValidation │ │
57
+ │ │ ToggleRenderer │ │ ├ FieldDisabled │ │
58
+ │ │ RadioRenderer │ │ ├ FieldLocked │ │
59
+ │ │ RadioGroupRenderer │ │ ├ FieldSubscribers │ │
60
+ │ │ LargeTextRenderer │ │ ├ FieldGoTo │ │
61
+ │ │ AddonRenderer │ │ └ FieldDescription │ │
62
+ │ └─────────────────────┘ └────────────────────────────────┘ │
63
+ └──────────────────────────────────┬───────────────────────────┘
64
+
65
+
66
+ ┌──────────────────────────────────────────────────────────────┐
67
+ │ BUSINESS LOGIC LAYER (bll/) │
68
+ │ │
69
+ │ BLL (Singleton Façade) │
70
+ │ ├── Formatter Factory → SSN / Phone / Number / Date / Zip │
71
+ │ │ Boolean / Text / Dropdown / Regex │
72
+ │ ├── Validator Factory → Required / Email / Date / Zip / │
73
+ │ │ MaxChar │
74
+ │ ├── SSF Service (Mixin-composed) │
75
+ │ │ ├ Loan → field values, metadata, lock, collections │
76
+ │ │ └ LoConnect → host operations, readonly, GTF, state DD │
77
+ │ └── Core → Logging, Exceptions, Utilities │
78
+ └──────────────────────────────────────────────────────────────┘
79
+ ```
80
+
81
+ ### Layer Breakdown
82
+
83
+ | Layer | Location | Responsibility |
84
+ | --------------- | ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
85
+ | **Entry Point** | `renderer/FieldRenderer.tsx` | Single public component. Delegates to `RendererFactory` based on `rendererType`. |
86
+ | **Renderer** | `renderer/` | UI rendering, event handling, ViewModel initialization. Each renderer extends the abstract `Renderer<T>` base class. |
87
+ | **Hooks** | `renderer/base/hooks/` | Composable state management classes mixed into a single `Hooks` class. Each hook handles one concern (metadata, value, validation, disabled, locked, subscribers, go-to, accessibility). |
88
+ | **BLL** | `bll/` | Business logic. Formatter and Validator factories, SSF data access, Core utilities. Acts as the single gateway between the renderer layer and external services. |
89
+ | **SSF** | `bll/ssf/` | Loan data operations. `Loan` mixin handles field CRUD; `LoConnect` mixin handles host-level operations (readonly status, Go-To-Field, state dropdown). |
90
+ | **Core** | `core/` | Cross-cutting concerns: structured logging and exception utilities. |
91
+
92
+ ### Key Design Patterns
93
+
94
+ | Pattern | Where Used | Why |
95
+ | ----------------------- | --------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
96
+ | **Factory** | `RendererFactory`, `FormatterFactory`, `ValidatorFactory` | Dynamic creation of renderers, formatters, and validators based on runtime type/acronym. Enables open-closed extensibility. |
97
+ | **Abstract Base Class** | `Renderer<T>` | Shared initialization (`initVM`), formatting/parsing, validation, BR wrapper, and prop sanitization across all renderers. |
98
+ | **Mixin Composition** | `Hooks`, `SSF` | Avoids deep class hierarchies. Each concern is a self-contained class mixed into a combined class using `@decorators.class.Mixins`. |
99
+ | **Singleton** | `BLL`, `SSF`, `FormatterFactory`, `Core` | Shared instances across renderers for consistent state and performance. |
100
+ | **Strategy** | Formatters, Validators | Each formatter/validator encapsulates a specific algorithm (e.g., `NumberFormatter` vs. `PhoneFormatter`). The factory selects the correct strategy at runtime. |
101
+ | **ViewModel** | `ViewModelT` | Encapsulates all reactive state a renderer needs, decoupling UI rendering from state management. |
102
+
103
+ ---
104
+
105
+ ## 3. High-Level Design
106
+
107
+ ### Component Hierarchy
108
+
109
+ ```
110
+ FieldRenderer (public API)
111
+ └─ RendererFactory.create(type)
112
+ └─ [SpecificRenderer].render() ← e.g., TextRenderer
113
+ ├─ BRWrapper (Business Rules) ← wraps the field for BR integration
114
+ │ └─ subscribes to loan change + loanUpdated events
115
+ ├─ AddonRenderer ← lock button + right addon slot
116
+ └─ DS Component ← e.g., DSInputText, DSCombobox
117
+ ├─ value ← formatted via Formatter
118
+ ├─ onChange → local state update + optional callback
119
+ ├─ onBlur → format → validate → parse → persist to SSF
120
+ ├─ disabled ← computed from metadata (readonly, locked)
121
+ └─ ARIA props ← built from field description
122
+ ```
123
+
124
+ ### ViewModel (`ViewModelT`)
125
+
126
+ Every renderer initializes a shared ViewModel through `initVM(props)`. The ViewModel contains:
127
+
128
+ | Property | Type | Source |
129
+ | ------------------ | --------------------------- | ---------------------------------------------------------------------------- |
130
+ | `fieldMetadata` | `FieldMetadataT` | Fetched from SSF via `FieldMeta` hook |
131
+ | `fieldValue` | `unknown` | Fetched from SSF, formatted for display |
132
+ | `setFieldValue` | state setter | React `useState` |
133
+ | `isLockedState` | `boolean` | SSF lock status via `FieldLocked` hook |
134
+ | `isFieldDisabled` | `boolean` | Computed: `isReadonlyLoan OR (isLockField AND !isLockedState) OR isReadOnly` |
135
+ | `isSearchedField` | `boolean` | Go-To-Field highlight via `FieldGoTo` hook |
136
+ | `validationResult` | `IValidationResult \| null` | Set on blur after running validators |
137
+ | `ariaDescProps` | `AriaDescT` | Built from label + field description via `FieldDescription` hook |
138
+
139
+ ### Initialization Sequence (inside `useFieldInit`)
140
+
141
+ ```
142
+ 1. Parallel fetch via Promise.all:
143
+ ├─ getMetadata(fieldId) → field format, options, readonly, lock, maxLength
144
+ ├─ isLoanReadonly() → loan-level readonly status
145
+ ├─ getFieldLockStatus(fieldId)→ field-level lock status
146
+ ├─ getFieldValue(fieldId) → raw field value from loan object
147
+ └─ isGoToField(fieldId) → whether this is the Go-To-Field target
148
+
149
+ 2. Post-fetch processing:
150
+ ├─ Format raw value using FormatterFactory → display value
151
+ ├─ Resolve lock state from metadata.isLockField + SSF lock status
152
+ ├─ Set readonly state
153
+ ├─ Set Go-To-Field highlight state
154
+ └─ Build ARIA description props from label + metadata.description
155
+ ```
156
+
157
+ ---
158
+
159
+ ## 4. Sequence Diagrams
160
+
161
+ ### 4.1 Mount Flow — Field Appears on Screen
162
+
163
+ ```mermaid
164
+ sequenceDiagram
165
+ participant Page as Page (Consumer)
166
+ participant FR as FieldRenderer
167
+ participant Factory as RendererFactory
168
+ participant TR as TextRenderer
169
+ participant Hooks as Hooks (Mixins)
170
+ participant BLL as BLL
171
+ participant SSF as SSF
172
+
173
+ Page->>FR: <FieldRenderer fieldId="FR0104" rendererType="text" />
174
+ FR->>Factory: create("text")
175
+ Factory-->>FR: TextRenderer.render() → FC
176
+
177
+ FR->>TR: render(props)
178
+ TR->>Hooks: initVM(props)
179
+
180
+ rect rgb(235, 245, 255)
181
+ Note over Hooks,SSF: Parallel fetch via Promise.all
182
+ Hooks->>SSF: getMetadata(fieldId)
183
+ Hooks->>SSF: getFieldValue(fieldId)
184
+ Hooks->>SSF: getLockStatus(fieldId)
185
+ Hooks->>SSF: isLoanReadonly()
186
+ Hooks->>SSF: isGoToField(fieldId)
187
+ SSF-->>Hooks: metadata, value, lock, readonly, goTo
188
+ end
189
+
190
+ Hooks->>BLL: formatValue(rawValue, fieldFormat)
191
+ BLL-->>Hooks: displayValue
192
+
193
+ Hooks->>Hooks: compute isFieldDisabled
194
+ Hooks->>Hooks: build ARIA props
195
+
196
+ Hooks-->>TR: ViewModel (value, metadata, disabled, locked, validation, aria)
197
+
198
+ TR->>TR: render BRWrapper → AddonRenderer → DSInputText
199
+ TR-->>Page: Rendered field on screen
200
+ ```
201
+
202
+ ### 4.2 onChange Flow — User Types
203
+
204
+ ```mermaid
205
+ sequenceDiagram
206
+ participant User
207
+ participant TR as TextRenderer
208
+ participant VM as ViewModel (state)
209
+ participant Page as Page (Consumer)
210
+
211
+ User->>TR: keystroke
212
+ TR->>TR: clear GoToField highlight
213
+ TR->>VM: setFieldValue(typedValue)
214
+ VM-->>TR: UI re-renders with new value
215
+
216
+ opt onChange callback provided
217
+ TR->>Page: onChange(fieldId, parsedValue)
218
+ end
219
+ ```
220
+
221
+ ### 4.3 onBlur Flow — User Leaves Field
222
+
223
+ ```mermaid
224
+ sequenceDiagram
225
+ participant User
226
+ participant TR as TextRenderer
227
+ participant BLL as BLL
228
+ participant Hooks as Hooks
229
+ participant SSF as SSF
230
+ participant Page as Page (Consumer)
231
+
232
+ User->>TR: blur (leaves field)
233
+
234
+ TR->>BLL: getFormattedValue(rawInput)
235
+ BLL-->>TR: formattedValue
236
+
237
+ TR->>Hooks: runValidationsOnValue(formattedValue, rules)
238
+
239
+ alt Validation fails
240
+ Hooks-->>TR: { isValid: false, message }
241
+ TR->>TR: setValidationResult → show error
242
+ Note over TR: STOP — do not save
243
+ else Validation passes
244
+ Hooks-->>TR: { isValid: true }
245
+ TR->>BLL: getParsedValue(formattedValue)
246
+ BLL-->>TR: parsedValue (raw for storage)
247
+
248
+ opt autoFieldSet = true (default)
249
+ TR->>SSF: setFieldValue(fieldId, parsedValue)
250
+ end
251
+
252
+ opt onBlur callback provided
253
+ TR->>Page: onBlur(fieldId, parsedValue)
254
+ end
255
+ end
256
+ ```
257
+
258
+ ### 4.4 External Change Flow — Live Sync
259
+
260
+ ```mermaid
261
+ sequenceDiagram
262
+ participant BR as Business Rule / Other Field
263
+ participant SSF as SSF
264
+ participant BRW as BRWrapper
265
+ participant TR as TextRenderer
266
+ participant BLL as BLL
267
+ participant VM as ViewModel (state)
268
+
269
+ BR->>SSF: modifies loan data
270
+
271
+ alt Specific field changed
272
+ SSF-)BRW: loan.change event (fieldId, oldVal, newVal)
273
+ Note over BRW: only reacts if fieldId matches & oldVal ≠ newVal
274
+ else Bulk loan update
275
+ SSF-)BRW: loan.loanUpdated event
276
+ end
277
+
278
+ BRW->>TR: refreshFieldFromLoan()
279
+ TR->>SSF: getFieldValue(fieldId)
280
+ SSF-->>TR: raw value
281
+ TR->>BLL: formatValue(rawValue, fieldFormat)
282
+ BLL-->>TR: displayValue
283
+ TR->>VM: setFieldValue(displayValue)
284
+ VM-->>TR: UI re-renders with updated value
285
+ ```
286
+
287
+ ### Key Lifecycle Events
288
+
289
+ | Event | What Happens |
290
+ | ---------------------- | --------------------------------------------------------------------------------------------------------- |
291
+ | **Mount** | `useFieldInit` fires → parallel SSF fetches → ViewModel populated → UI renders |
292
+ | **onChange** | Local state updated immediately → optional `props.onChange` callback fired |
293
+ | **onBlur** | Format → Validate → (if invalid, show error & stop) → Parse → Persist to SSF → `props.onBlur` callback |
294
+ | **Loan Change Event** | BRWrapper detects `change` event for this `fieldId` → re-fetches value from SSF → re-formats → updates UI |
295
+ | **Loan Updated Event** | BRWrapper detects `loanUpdated` → re-fetches and refreshes all field data |
296
+ | **Lock Toggle** | `callCalcForLock()` on SSF → toggle `isLockedState` → recompute `isFieldDisabled` |
297
+
298
+ ---
299
+
300
+ ## 5. Smart Control Capabilities via Field Metadata
301
+
302
+ Field metadata (`FieldMetadataT`) is fetched once per field from SSF and drives virtually all behavior. Below is each metadata property and what the smart controls do with it.
303
+
304
+ ### 5.1 Format-Driven Rendering (`fieldFormat`)
305
+
306
+ The `fieldFormat` acronym (e.g., `"D2"`, `"SS"`, `"ST"`, `"P"`) determines:
307
+
308
+ | Acronym | Format | Formatter Used | Behavior |
309
+ | ----------------------- | ------------------- | ------------------- | --------------------------------------------- |
310
+ | `SS` | SSN | `SsnFormatter` | Masks as `***-**-1234`, parses to raw digits |
311
+ | `P` | Phone | `PhoneFormatter` | Formats as `(123) 456-7890`, parses to digits |
312
+ | `I` | Integer | `NumberFormatter` | No decimal, thousands separator |
313
+ | `D2`–`D6` | Decimal (2-6) | `NumberFormatter` | Fixed decimal places, thousands separator |
314
+ | `T` | Date | `DateFormatter` | Formats to `MM/DD/YYYY` or custom format |
315
+ | `TD` | DateTime | `DateFormatter` | Date + time formatting |
316
+ | `Y` | Yes/No | `BooleanFormatter` | Converts `"Y"`/`"N"` ↔ `true`/`false` |
317
+ | `S` | String | `TextFormatter` | Pass-through |
318
+ | `ST` | State | `DropdownFormatter` | Auto-fetches state dropdown list from SSF |
319
+ | `Z` | Zip Code | `ZipFormatter` | 5 or 9-digit zip formatting |
320
+ | `RS`, `RI`, `RD2`–`RD6` | Read-Ahead variants | Same as base | Used for collection/RA fields |
321
+
322
+ The `FormatterFactory` maps acronyms to formatter instances. Each formatter implements `IFormatter<T, U>` with `format()` and `parse()` methods, enabling bidirectional conversion between display values and raw loan data.
323
+
324
+ ### 5.2 Renderer–Format Compatibility Validation
325
+
326
+ `RendererValidator` ensures the `rendererType` is compatible with the `fieldFormat`:
327
+
328
+ - **number** renderer requires numeric formats (`DECIMAL_*`, `INTEGER`)
329
+ - **checkbox** renderer requires boolean formats (`YN`, `BOOLEAN`)
330
+ - **date** renderer requires date formats (`DATE`, `DATETIME`)
331
+ - **text/email** renderers accept anything except numeric, boolean, and date formats
332
+
333
+ A mismatch throws an error with a diagnostic log, preventing subtle bugs from incorrect configuration.
334
+
335
+ ### 5.3 Dropdown Options (`fieldOptions`)
336
+
337
+ - If the metadata includes `fieldOptions`, they are mapped to `{ dsId, type, label, value }` objects for the `DSCombobox` component.
338
+ - **Special case**: When `fieldFormat === "ST"` (State), the system auto-fetches the state dropdown list from SSF via `getStateDropDownList()` and memoizes the result.
339
+ - Consumers can also pass custom `options` via props for non-metadata-driven dropdowns.
340
+
341
+ ### 5.4 Lock/Unlock Field (`isLockField`)
342
+
343
+ When `isLockField` is `true` in metadata:
344
+
345
+ - The `AddonRenderer` displays a lock/unlock button next to the field
346
+ - Lock status is fetched from SSF via `getLockStatus(fieldId)`
347
+ - Toggling calls `SSF.callCalcForLock(fieldId, isLockedState)` which triggers a server-side calculation
348
+ - **Locked fields are disabled**: `isFieldDisabled = isLockField && !isLockedState`
349
+
350
+ ### 5.5 Read-Only State (`isReadOnly`)
351
+
352
+ Two levels of read-only are checked:
353
+
354
+ - **Field-level**: `metadata.isReadOnly` — this specific field is read-only
355
+ - **Loan-level**: `SSF.isReadOnlyLoan()` — the entire loan is in read-only mode
356
+
357
+ The combined disabled state is: `isReadonlyLoan || (isLockField && !isLockedState) || isReadOnly`
358
+
359
+ ### 5.6 Max Length Enforcement (`maxLength`)
360
+
361
+ - For **text** fields: applied directly as the HTML `maxLength` attribute
362
+ - For **number** fields: adjusted to account for prefix, suffix, thousands separators, and decimal points. The system calculates `totalMaxLength = integerPartLength + prefixLength + decimalSeparator + decimalPlaces + suffixLength`
363
+
364
+ ### 5.7 Accessibility (`description`)
365
+
366
+ The `description` field from metadata is used to build ARIA attributes:
367
+
368
+ - If the field has a visible `label` → `aria-description` is set to the metadata description
369
+ - If the field has no label → `aria-label` is set to the metadata description
370
+ - This ensures screen readers always have meaningful context for every field
371
+
372
+ ### 5.8 Contract Path & Collection Support (`contractPath`)
373
+
374
+ - Each field maps to a canonical loan contract path (e.g., `"Loan.LoanAmount"`)
375
+ - For **collection fields**, the contract path contains a `[%]` placeholder (e.g., `"Loan.Borrowers[%].FirstName"`) which is resolved to the actual row index at runtime via `SSF.getCollectionRowIndex()`
376
+
377
+ ### 5.9 Nullable Fields (`isNullable`)
378
+
379
+ The `isNullable` flag from metadata indicates whether a field can hold `null`/empty values. This is used in conjunction with validation rules (e.g., the `required` validator checks this).
380
+
381
+ ### 5.10 Validation Rules
382
+
383
+ Validation is consumer-configured via `validationRules` prop but integrates with metadata:
384
+
385
+ | Validator | Trigger | Behavior |
386
+ | ---------- | --------------------------------------------- | ----------------------------------- |
387
+ | `required` | Blur | Fails if value is empty/null |
388
+ | `email` | Blur (auto-added for `rendererType: "email"`) | Regex-based email validation |
389
+ | `date` | Blur | Validates date format and range |
390
+ | `zip` | Blur | Validates 5 or 9-digit zip format |
391
+ | `maxChar` | Blur | Checks against `options.charsLimit` |
392
+
393
+ Validators run sequentially; the first failure stops execution and displays the error message via `validationMessage` and `hasError` props on the BR wrapper.
394
+
395
+ ### 5.11 Go-To-Field Highlighting
396
+
397
+ The system integrates with the host application's "Go-To-Field" (GTF) navigation:
398
+
399
+ - On mount, checks if this field is the GTF target via `SSF.getGTFSelectedFormDetails()`
400
+ - If matched, sets `isSearchedField = true` → applies `withHighlight` styling on the BR wrapper
401
+ - On user interaction (`onChange`), the highlight is cleared
402
+
403
+ ### 5.12 Prop Sanitization
404
+
405
+ The `sanitize()` method on the base `Renderer` class filters out "controlled props" from the `extra` prop bag. This prevents consumers from accidentally overriding internally-managed props like `onChange`, `onBlur`, `value`, etc. The controlled props per renderer type are defined in `RENDERER_CONTROLLED_PROPS`.
406
+
407
+ ### 5.13 Live Loan Synchronization
408
+
409
+ Every rendered field subscribes to two SSF events through the `BRWrapper`:
410
+
411
+ - **`loan.change`** — fired when a specific field changes; if the changed field ID matches, re-fetches and re-formats the value
412
+ - **`loan.loanUpdated`** — fired on bulk loan updates; re-fetches all field data
413
+
414
+ This ensures fields stay in sync when business rules, calculations, or other UI interactions modify loan data externally.
415
+
416
+ ### 5.14 Auto Field Set (`autoFieldSet`)
417
+
418
+ The `autoFieldSet` flag (default `true`) controls whether the renderer automatically persists the parsed value to SSF on blur. When set to `false`, the renderer only updates local state and calls the `onBlur` callback, letting the consumer handle persistence manually.
419
+
420
+ ---
421
+
422
+ ## Summary
423
+
424
+ | Aspect | Implementation |
425
+ | ---------------------- | ------------------------------------------------------------- |
426
+ | **Entry Point** | `<FieldRenderer fieldId={...} rendererType={...} />` |
427
+ | **Renderer Selection** | Factory pattern → 12 renderer types |
428
+ | **State Management** | ViewModel pattern with React hooks composed via Mixins |
429
+ | **Data Access** | SSF abstraction layer (Loan + LoConnect mixins) |
430
+ | **Formatting** | Bidirectional format/parse via strategy-based formatters |
431
+ | **Validation** | Pluggable validator chain via factory |
432
+ | **Accessibility** | ARIA attributes auto-generated from metadata |
433
+ | **Synchronization** | Event-driven via SSF `change` and `loanUpdated` subscriptions |
434
+ | **Extensibility** | Add new renderer → extend `Renderer<T>`, register in factory |