@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.
- package/dist/cjs/ARCHITECTURE.md +434 -0
- package/dist/cjs/OVERVIEW.md +229 -0
- package/dist/cjs/bll/constants.js +86 -0
- package/dist/cjs/bll/formatters/booleanFormatter.js +51 -0
- package/dist/cjs/bll/formatters/dateFormatter.js +78 -0
- package/dist/cjs/bll/formatters/dropdownFormatter.js +34 -0
- package/dist/cjs/bll/formatters/factory/index.js +115 -0
- package/dist/cjs/bll/formatters/index.js +24 -0
- package/dist/cjs/bll/formatters/numberFormatter.js +70 -0
- package/dist/cjs/bll/formatters/phoneFormatter.js +57 -0
- package/dist/cjs/bll/formatters/regexFormatter.js +52 -0
- package/dist/cjs/bll/formatters/ssnFormatter.js +50 -0
- package/dist/cjs/bll/formatters/textFormatter.js +43 -0
- package/dist/cjs/bll/formatters/zipFormatter.js +48 -0
- package/dist/cjs/bll/index.js +62 -0
- package/dist/cjs/bll/ssf/index.js +48 -0
- package/dist/cjs/bll/ssf/loan.js +81 -0
- package/dist/cjs/bll/ssf/loconnect.js +70 -0
- package/dist/cjs/bll/ssf/ssfBase.js +97 -0
- package/dist/cjs/bll/ssf/types.js +16 -0
- package/dist/cjs/bll/types.js +16 -0
- package/dist/cjs/bll/validators/dateValidator.js +60 -0
- package/dist/cjs/bll/validators/emailValidator.js +47 -0
- package/dist/cjs/bll/validators/factory/index.js +81 -0
- package/dist/cjs/bll/validators/index.js +24 -0
- package/dist/cjs/bll/validators/maxCharValidator.js +49 -0
- package/dist/cjs/bll/validators/requiredValidator.js +44 -0
- package/dist/cjs/bll/validators/zipValidator.js +53 -0
- package/dist/cjs/core/index.js +52 -0
- package/dist/cjs/demo/config.js +391 -0
- package/dist/cjs/demo/index.js +31 -0
- package/dist/cjs/package.json +7 -0
- package/dist/cjs/renderer/FieldRenderer.js +45 -0
- package/dist/cjs/renderer/base/hooks/fieldDescription.js +39 -0
- package/dist/cjs/renderer/base/hooks/fieldDisabled.js +53 -0
- package/dist/cjs/renderer/base/hooks/fieldGoTo.js +50 -0
- package/dist/cjs/renderer/base/hooks/fieldLocked.js +42 -0
- package/dist/cjs/renderer/base/hooks/fieldMeta.js +150 -0
- package/dist/cjs/renderer/base/hooks/fieldSubscribers.js +66 -0
- package/dist/cjs/renderer/base/hooks/fieldValidation.js +45 -0
- package/dist/cjs/renderer/base/hooks/fieldValue.js +215 -0
- package/dist/cjs/renderer/base/hooks/hookBase.js +29 -0
- package/dist/cjs/renderer/base/hooks/index.js +139 -0
- package/dist/cjs/renderer/base/renderer.js +198 -0
- package/dist/cjs/renderer/base/rendererValidator.js +97 -0
- package/dist/cjs/renderer/factory/index.js +58 -0
- package/dist/cjs/renderer/field-renderers/AddonRenderer.js +75 -0
- package/dist/cjs/renderer/field-renderers/CheckboxRenderer.js +123 -0
- package/dist/cjs/renderer/field-renderers/DateRenderer.js +206 -0
- package/dist/cjs/renderer/field-renderers/DropdownRenderer/hook.js +99 -0
- package/dist/cjs/renderer/field-renderers/DropdownRenderer/index.js +216 -0
- package/dist/cjs/renderer/field-renderers/LargeTextRenderer.js +209 -0
- package/dist/cjs/renderer/field-renderers/NumberRenderer.js +216 -0
- package/dist/cjs/renderer/field-renderers/RadioGroupRenderer.js +128 -0
- package/dist/cjs/renderer/field-renderers/RadioRenderer.js +121 -0
- package/dist/cjs/renderer/field-renderers/TextRenderer.js +223 -0
- package/dist/cjs/renderer/field-renderers/ToggleRenderer.js +121 -0
- package/dist/cjs/renderer/field-renderers/ZipCodeRenderer/helper.js +132 -0
- package/dist/cjs/renderer/field-renderers/ZipCodeRenderer/hook.js +128 -0
- package/dist/cjs/renderer/field-renderers/ZipCodeRenderer/index.js +273 -0
- package/dist/cjs/renderer/index.js +24 -0
- package/dist/cjs/renderer/styles.js +51 -0
- package/dist/cjs/renderer/types.js +16 -0
- package/dist/cjs/tests/base/flowBase.js +125 -0
- package/dist/cjs/tests/base/index.js +52 -0
- package/dist/cjs/tests/flows/checkboxRendererFlows.js +85 -0
- package/dist/cjs/tests/flows/dateRendererFlows.js +870 -0
- package/dist/cjs/tests/flows/dropdownRendererFlows.js +591 -0
- package/dist/cjs/tests/flows/largeTextRendererFlows.js +99 -0
- package/dist/cjs/tests/flows/numberRendererFlows.js +175 -0
- package/dist/cjs/tests/flows/radioRendererFlows.js +115 -0
- package/dist/cjs/tests/flows/textRendererFlows.js +349 -0
- package/dist/cjs/tests/flows/toggleRendererFlows.js +106 -0
- package/dist/cjs/tests/flows/zipCodeRendererFlows.js +1163 -0
- package/dist/cjs/utils/dateHelper.js +65 -0
- package/dist/esm/ARCHITECTURE.md +434 -0
- package/dist/esm/OVERVIEW.md +229 -0
- package/dist/esm/bll/constants.js +66 -0
- package/dist/esm/bll/formatters/booleanFormatter.js +33 -0
- package/dist/esm/bll/formatters/dateFormatter.js +48 -0
- package/dist/esm/bll/formatters/dropdownFormatter.js +14 -0
- package/dist/esm/bll/formatters/factory/index.js +97 -0
- package/dist/esm/bll/formatters/index.js +4 -0
- package/dist/esm/bll/formatters/numberFormatter.js +54 -0
- package/dist/esm/bll/formatters/phoneFormatter.js +41 -0
- package/dist/esm/bll/formatters/regexFormatter.js +34 -0
- package/dist/esm/bll/formatters/ssnFormatter.js +32 -0
- package/dist/esm/bll/formatters/textFormatter.js +25 -0
- package/dist/esm/bll/formatters/zipFormatter.js +30 -0
- package/dist/esm/bll/index.js +44 -0
- package/dist/esm/bll/ssf/index.js +30 -0
- package/dist/esm/bll/ssf/loan.js +63 -0
- package/dist/esm/bll/ssf/loconnect.js +52 -0
- package/dist/esm/bll/ssf/ssfBase.js +67 -0
- package/dist/esm/bll/ssf/types.js +0 -0
- package/dist/esm/bll/types.js +0 -0
- package/dist/esm/bll/validators/dateValidator.js +30 -0
- package/dist/esm/bll/validators/emailValidator.js +29 -0
- package/dist/esm/bll/validators/factory/index.js +63 -0
- package/dist/esm/bll/validators/index.js +4 -0
- package/dist/esm/bll/validators/maxCharValidator.js +31 -0
- package/dist/esm/bll/validators/requiredValidator.js +26 -0
- package/dist/esm/bll/validators/zipValidator.js +35 -0
- package/dist/esm/core/index.js +34 -0
- package/dist/esm/demo/config.js +371 -0
- package/dist/esm/demo/index.js +11 -0
- package/dist/esm/package.json +7 -0
- package/dist/esm/renderer/FieldRenderer.js +15 -0
- package/dist/esm/renderer/base/hooks/fieldDescription.js +19 -0
- package/dist/esm/renderer/base/hooks/fieldDisabled.js +33 -0
- package/dist/esm/renderer/base/hooks/fieldGoTo.js +30 -0
- package/dist/esm/renderer/base/hooks/fieldLocked.js +22 -0
- package/dist/esm/renderer/base/hooks/fieldMeta.js +132 -0
- package/dist/esm/renderer/base/hooks/fieldSubscribers.js +36 -0
- package/dist/esm/renderer/base/hooks/fieldValidation.js +25 -0
- package/dist/esm/renderer/base/hooks/fieldValue.js +195 -0
- package/dist/esm/renderer/base/hooks/hookBase.js +9 -0
- package/dist/esm/renderer/base/hooks/index.js +121 -0
- package/dist/esm/renderer/base/renderer.js +178 -0
- package/dist/esm/renderer/base/rendererValidator.js +77 -0
- package/dist/esm/renderer/factory/index.js +38 -0
- package/dist/esm/renderer/field-renderers/AddonRenderer.js +55 -0
- package/dist/esm/renderer/field-renderers/CheckboxRenderer.js +93 -0
- package/dist/esm/renderer/field-renderers/DateRenderer.js +176 -0
- package/dist/esm/renderer/field-renderers/DropdownRenderer/hook.js +79 -0
- package/dist/esm/renderer/field-renderers/DropdownRenderer/index.js +186 -0
- package/dist/esm/renderer/field-renderers/LargeTextRenderer.js +179 -0
- package/dist/esm/renderer/field-renderers/NumberRenderer.js +188 -0
- package/dist/esm/renderer/field-renderers/RadioGroupRenderer.js +108 -0
- package/dist/esm/renderer/field-renderers/RadioRenderer.js +91 -0
- package/dist/esm/renderer/field-renderers/TextRenderer.js +197 -0
- package/dist/esm/renderer/field-renderers/ToggleRenderer.js +91 -0
- package/dist/esm/renderer/field-renderers/ZipCodeRenderer/helper.js +112 -0
- package/dist/esm/renderer/field-renderers/ZipCodeRenderer/hook.js +108 -0
- package/dist/esm/renderer/field-renderers/ZipCodeRenderer/index.js +247 -0
- package/dist/esm/renderer/index.js +4 -0
- package/dist/esm/renderer/styles.js +21 -0
- package/dist/esm/renderer/types.js +0 -0
- package/dist/esm/tests/base/flowBase.js +105 -0
- package/dist/esm/tests/base/index.js +22 -0
- package/dist/esm/tests/flows/checkboxRendererFlows.js +65 -0
- package/dist/esm/tests/flows/dateRendererFlows.js +850 -0
- package/dist/esm/tests/flows/dropdownRendererFlows.js +571 -0
- package/dist/esm/tests/flows/largeTextRendererFlows.js +79 -0
- package/dist/esm/tests/flows/numberRendererFlows.js +155 -0
- package/dist/esm/tests/flows/radioRendererFlows.js +95 -0
- package/dist/esm/tests/flows/textRendererFlows.js +329 -0
- package/dist/esm/tests/flows/toggleRendererFlows.js +86 -0
- package/dist/esm/tests/flows/zipCodeRendererFlows.js +1143 -0
- package/dist/esm/utils/dateHelper.js +35 -0
- package/dist/types/lib/bll/constants.d.ts +9 -0
- package/dist/types/lib/bll/formatters/booleanFormatter.d.ts +5 -0
- package/dist/types/lib/bll/formatters/dateFormatter.d.ts +28 -0
- package/dist/types/lib/bll/formatters/dropdownFormatter.d.ts +6 -0
- package/dist/types/lib/bll/formatters/factory/index.d.ts +71 -0
- package/dist/types/lib/bll/formatters/index.d.ts +2 -0
- package/dist/types/lib/bll/formatters/numberFormatter.d.ts +6 -0
- package/dist/types/lib/bll/formatters/phoneFormatter.d.ts +7 -0
- package/dist/types/lib/bll/formatters/regexFormatter.d.ts +5 -0
- package/dist/types/lib/bll/formatters/ssnFormatter.d.ts +5 -0
- package/dist/types/lib/bll/formatters/textFormatter.d.ts +6 -0
- package/dist/types/lib/bll/formatters/zipFormatter.d.ts +5 -0
- package/dist/types/lib/bll/index.d.ts +20 -0
- package/dist/types/lib/bll/ssf/index.d.ts +25 -0
- package/dist/types/lib/bll/ssf/loan.d.ts +16 -0
- package/dist/types/lib/bll/ssf/loconnect.d.ts +15 -0
- package/dist/types/lib/bll/ssf/ssfBase.d.ts +23 -0
- package/dist/types/lib/bll/ssf/types.d.ts +99 -0
- package/dist/types/lib/bll/types.d.ts +47 -0
- package/dist/types/lib/bll/validators/dateValidator.d.ts +16 -0
- package/dist/types/lib/bll/validators/emailValidator.d.ts +4 -0
- package/dist/types/lib/bll/validators/factory/index.d.ts +15 -0
- package/dist/types/lib/bll/validators/index.d.ts +2 -0
- package/dist/types/lib/bll/validators/maxCharValidator.d.ts +4 -0
- package/dist/types/lib/bll/validators/requiredValidator.d.ts +4 -0
- package/dist/types/lib/bll/validators/zipValidator.d.ts +5 -0
- package/dist/types/lib/core/index.d.ts +29 -0
- package/dist/types/lib/demo/config.d.ts +11 -0
- package/dist/types/lib/demo/index.d.ts +1 -0
- package/dist/types/lib/renderer/FieldRenderer.d.ts +5 -0
- package/dist/types/lib/renderer/base/hooks/fieldDescription.d.ts +5 -0
- package/dist/types/lib/renderer/base/hooks/fieldDisabled.d.ts +10 -0
- package/dist/types/lib/renderer/base/hooks/fieldGoTo.d.ts +4 -0
- package/dist/types/lib/renderer/base/hooks/fieldLocked.d.ts +4 -0
- package/dist/types/lib/renderer/base/hooks/fieldMeta.d.ts +10 -0
- package/dist/types/lib/renderer/base/hooks/fieldSubscribers.d.ts +6 -0
- package/dist/types/lib/renderer/base/hooks/fieldValidation.d.ts +9 -0
- package/dist/types/lib/renderer/base/hooks/fieldValue.d.ts +31 -0
- package/dist/types/lib/renderer/base/hooks/hookBase.d.ts +9 -0
- package/dist/types/lib/renderer/base/hooks/index.d.ts +19 -0
- package/dist/types/lib/renderer/base/renderer.d.ts +43 -0
- package/dist/types/lib/renderer/base/rendererValidator.d.ts +15 -0
- package/dist/types/lib/renderer/factory/index.d.ts +5 -0
- package/dist/types/lib/renderer/field-renderers/AddonRenderer.d.ts +12 -0
- package/dist/types/lib/renderer/field-renderers/CheckboxRenderer.d.ts +7 -0
- package/dist/types/lib/renderer/field-renderers/DateRenderer.d.ts +13 -0
- package/dist/types/lib/renderer/field-renderers/DropdownRenderer/hook.d.ts +23 -0
- package/dist/types/lib/renderer/field-renderers/DropdownRenderer/index.d.ts +12 -0
- package/dist/types/lib/renderer/field-renderers/LargeTextRenderer.d.ts +17 -0
- package/dist/types/lib/renderer/field-renderers/NumberRenderer.d.ts +12 -0
- package/dist/types/lib/renderer/field-renderers/RadioGroupRenderer.d.ts +8 -0
- package/dist/types/lib/renderer/field-renderers/RadioRenderer.d.ts +8 -0
- package/dist/types/lib/renderer/field-renderers/TextRenderer.d.ts +8 -0
- package/dist/types/lib/renderer/field-renderers/ToggleRenderer.d.ts +24 -0
- package/dist/types/lib/renderer/field-renderers/ZipCodeRenderer/helper.d.ts +48 -0
- package/dist/types/lib/renderer/field-renderers/ZipCodeRenderer/hook.d.ts +17 -0
- package/dist/types/lib/renderer/field-renderers/ZipCodeRenderer/index.d.ts +9 -0
- package/dist/types/lib/renderer/index.d.ts +2 -0
- package/dist/types/lib/renderer/styles.d.ts +7 -0
- package/dist/types/lib/renderer/types.d.ts +325 -0
- package/dist/types/lib/tests/base/flowBase.d.ts +13 -0
- package/dist/types/lib/tests/base/index.d.ts +6 -0
- package/dist/types/lib/tests/flows/checkboxRendererFlows.d.ts +9 -0
- package/dist/types/lib/tests/flows/dateRendererFlows.d.ts +120 -0
- package/dist/types/lib/tests/flows/dropdownRendererFlows.d.ts +92 -0
- package/dist/types/lib/tests/flows/largeTextRendererFlows.d.ts +9 -0
- package/dist/types/lib/tests/flows/numberRendererFlows.d.ts +11 -0
- package/dist/types/lib/tests/flows/radioRendererFlows.d.ts +10 -0
- package/dist/types/lib/tests/flows/textRendererFlows.d.ts +16 -0
- package/dist/types/lib/tests/flows/toggleRendererFlows.d.ts +10 -0
- package/dist/types/lib/tests/flows/zipCodeRendererFlows.d.ts +169 -0
- package/dist/types/lib/tests/loan-field-renderer-flows.test.d.ts +1 -0
- package/dist/types/lib/utils/dateHelper.d.ts +8 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -0
- package/package.json +103 -0
|
@@ -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 |
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
# Loan Field Renderers — Overview
|
|
2
|
+
|
|
3
|
+
## What Is It?
|
|
4
|
+
|
|
5
|
+
A **metadata-driven smart control system** that renders loan form fields. Instead of each page manually handling data fetching, formatting, validation, locking, and persistence — a single component does it all.
|
|
6
|
+
|
|
7
|
+
**Before (without smart controls):**
|
|
8
|
+
Each page manually wires up SSF calls, formatting logic, validation, lock handling, accessibility, and event subscriptions for every field.
|
|
9
|
+
|
|
10
|
+
**After (with smart controls):**
|
|
11
|
+
|
|
12
|
+
```jsx
|
|
13
|
+
<FieldRenderer fieldId="FR0104" rendererType="number" />
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
One line. The system handles everything else automatically.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## What Does It Do Automatically?
|
|
21
|
+
|
|
22
|
+
| Capability | Description |
|
|
23
|
+
| ----------------- | -------------------------------------------------------------------------------- |
|
|
24
|
+
| **Data Fetch** | Fetches field metadata + current value from SSF on mount |
|
|
25
|
+
| **Formatting** | Applies the correct display format (currency, phone, SSN, date, zip) |
|
|
26
|
+
| **Input Masking** | Enforces input patterns (phone mask, SSN mask, number mask) |
|
|
27
|
+
| **Validation** | Runs configurable validation rules on blur (required, email, date, zip, maxChar) |
|
|
28
|
+
| **Persistence** | Parses display value back to raw data and saves to loan on blur |
|
|
29
|
+
| **Lock/Unlock** | Shows lock button, manages lock state, disables field when locked |
|
|
30
|
+
| **Read-Only** | Respects both field-level and loan-level read-only states |
|
|
31
|
+
| **Live Sync** | Subscribes to loan events — auto-refreshes when data changes externally |
|
|
32
|
+
| **Accessibility** | Builds ARIA attributes from field description metadata |
|
|
33
|
+
| **Go-To-Field** | Highlights the field when navigated to via Go-To-Field |
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Supported Renderer Types
|
|
38
|
+
|
|
39
|
+
| Type | UI Component | Use Case |
|
|
40
|
+
| ------------ | ------------------- | --------------------------------------------------------- |
|
|
41
|
+
| `text` | Text input | Names, addresses, general text (supports Phone/SSN masks) |
|
|
42
|
+
| `number` | Masked number input | Amounts, rates, percentages |
|
|
43
|
+
| `date` | Date picker | Dates with configurable format |
|
|
44
|
+
| `dropdown` | Combobox | Picklists, state selection |
|
|
45
|
+
| `zipcode` | Autocomplete input | Zip codes with geo-lookup |
|
|
46
|
+
| `checkbox` | Checkbox | Yes/No boolean fields |
|
|
47
|
+
| `toggle` | Toggle switch | On/Off boolean fields |
|
|
48
|
+
| `radio` | Single radio button | One option in a group |
|
|
49
|
+
| `radiogroup` | Radio button group | Multiple exclusive options |
|
|
50
|
+
| `largetext` | Textarea | Comments, large text with char counter |
|
|
51
|
+
| `button` | Button | Action triggers |
|
|
52
|
+
| `email` | Text input | Email with built-in email validation |
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## How It Works — 3 Phases
|
|
57
|
+
|
|
58
|
+
### Phase 1: Mount — "Set Up the Field"
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
Consumer passes fieldId + rendererType
|
|
62
|
+
│
|
|
63
|
+
▼
|
|
64
|
+
RendererFactory picks the right renderer
|
|
65
|
+
│
|
|
66
|
+
▼
|
|
67
|
+
Parallel SSF calls (all at once):
|
|
68
|
+
┌─────────────────────────────────┐
|
|
69
|
+
│ • Field metadata (format, │
|
|
70
|
+
│ options, readonly, lock, │
|
|
71
|
+
│ maxLength, description) │
|
|
72
|
+
│ • Current field value │
|
|
73
|
+
│ • Lock status │
|
|
74
|
+
│ • Loan readonly status │
|
|
75
|
+
│ • Go-To-Field status │
|
|
76
|
+
└─────────────────────────────────┘
|
|
77
|
+
│
|
|
78
|
+
▼
|
|
79
|
+
Build ViewModel → Render UI
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Phase 2: User Interaction — "Handle Input"
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
User types → onChange
|
|
86
|
+
┌────────────────────────────┐
|
|
87
|
+
│ Update local display value │
|
|
88
|
+
│ Fire optional callback │
|
|
89
|
+
└────────────────────────────┘
|
|
90
|
+
|
|
91
|
+
User leaves field → onBlur
|
|
92
|
+
┌────────────────────────────┐
|
|
93
|
+
│ 1. Format the value │
|
|
94
|
+
│ 2. Validate │
|
|
95
|
+
│ └─ Invalid? Show error, │
|
|
96
|
+
│ stop here │
|
|
97
|
+
│ 3. Parse to raw value │
|
|
98
|
+
│ 4. Save to loan via SSF │
|
|
99
|
+
│ 5. Fire optional callback │
|
|
100
|
+
└────────────────────────────┘
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Phase 3: External Changes — "Stay in Sync"
|
|
104
|
+
|
|
105
|
+
```
|
|
106
|
+
Business rule or another field changes loan data
|
|
107
|
+
│
|
|
108
|
+
▼
|
|
109
|
+
SSF fires "change" or "loanUpdated" event
|
|
110
|
+
│
|
|
111
|
+
▼
|
|
112
|
+
Field re-fetches its value from SSF
|
|
113
|
+
│
|
|
114
|
+
▼
|
|
115
|
+
Re-formats and updates display automatically
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## Architecture — 3 Layers
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
┌───────────────────────────────────────────┐
|
|
124
|
+
│ RENDERER LAYER │
|
|
125
|
+
│ UI components, event handling, ViewModel │
|
|
126
|
+
│ 12 specific renderers + shared base class │
|
|
127
|
+
├───────────────────────────────────────────┤
|
|
128
|
+
│ BUSINESS LOGIC LAYER │
|
|
129
|
+
│ Formatters (10 types) — format ↔ parse │
|
|
130
|
+
│ Validators (5 types) — validate on blur │
|
|
131
|
+
│ SSF Service — loan data access & events │
|
|
132
|
+
├───────────────────────────────────────────┤
|
|
133
|
+
│ DATA ACCESS LAYER (SSF) │
|
|
134
|
+
│ Field metadata, values, lock status │
|
|
135
|
+
│ Loan readonly, Go-To-Field, state list │
|
|
136
|
+
│ Event subscriptions (change, loanUpdated) │
|
|
137
|
+
└───────────────────────────────────────────┘
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
**Key design decisions:**
|
|
141
|
+
|
|
142
|
+
- **Factory Pattern** — Renderers, formatters, and validators are created dynamically by type
|
|
143
|
+
- **Mixin Composition** — Hooks and SSF combine focused classes instead of deep inheritance
|
|
144
|
+
- **ViewModel Pattern** — All reactive state lives in one object, decoupled from UI
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## How Metadata Drives Everything
|
|
149
|
+
|
|
150
|
+
When a field is loaded, its metadata from SSF determines behavior automatically:
|
|
151
|
+
|
|
152
|
+
```
|
|
153
|
+
fieldFormat: "D2" → NumberFormatter with 2 decimal places
|
|
154
|
+
fieldFormat: "SS" → SSN mask (***-**-1234)
|
|
155
|
+
fieldFormat: "ST" → Auto-fetch state dropdown list
|
|
156
|
+
fieldFormat: "P" → Phone mask ((123) 456-7890)
|
|
157
|
+
|
|
158
|
+
isLockField: true → Show lock button, disable when locked
|
|
159
|
+
isReadOnly: true → Disable the field
|
|
160
|
+
isReadonlyLoan → Loan-level check — if loan is disabled, all fields disable
|
|
161
|
+
maxLength: 50 → Enforce character limit
|
|
162
|
+
description: "..." → Set ARIA label/description
|
|
163
|
+
fieldOptions: [...] → Populate dropdown choices
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
No conditional logic needed in consuming pages — the metadata configures the control.
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## Consumer API — Quick Reference
|
|
171
|
+
|
|
172
|
+
### Basic Usage
|
|
173
|
+
|
|
174
|
+
```jsx
|
|
175
|
+
// Simple text field
|
|
176
|
+
<FieldRenderer fieldId="FR0001" rendererType="text" label="First Name" />
|
|
177
|
+
|
|
178
|
+
// Number with label
|
|
179
|
+
<FieldRenderer fieldId="FR0104" rendererType="number" label="Loan Amount" />
|
|
180
|
+
|
|
181
|
+
// Dropdown
|
|
182
|
+
<FieldRenderer fieldId="FR0200" rendererType="dropdown" label="State" />
|
|
183
|
+
|
|
184
|
+
// Date
|
|
185
|
+
<FieldRenderer fieldId="FR0300" rendererType="date" label="Closing Date" />
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### With Optional Props
|
|
189
|
+
|
|
190
|
+
```jsx
|
|
191
|
+
<FieldRenderer
|
|
192
|
+
fieldId="FR0104"
|
|
193
|
+
rendererType="number"
|
|
194
|
+
label="Loan Amount"
|
|
195
|
+
onBlur={(fieldId, value) => console.log('saved', value)}
|
|
196
|
+
onChange={(fieldId, value) => console.log('changed', value)}
|
|
197
|
+
validationRules={[{ type: 'required', message: 'Amount is required' }]}
|
|
198
|
+
autoFieldSet={true} // auto-persist on blur (default: true)
|
|
199
|
+
isDisable={false} // consumer-controlled disable
|
|
200
|
+
forceRefresh={refreshFlag} // trigger re-fetch from SSF
|
|
201
|
+
/>
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Common Props
|
|
205
|
+
|
|
206
|
+
| Prop | Type | Purpose |
|
|
207
|
+
| ----------------- | ----------- | ------------------------------------------------- |
|
|
208
|
+
| `fieldId` | `string` | **(Required)** SSF field identifier |
|
|
209
|
+
| `rendererType` | `string` | **(Required)** Which UI control to render |
|
|
210
|
+
| `label` | `string` | Field label text |
|
|
211
|
+
| `onBlur` | `function` | Callback after value is validated and parsed |
|
|
212
|
+
| `onChange` | `function` | Callback on every value change |
|
|
213
|
+
| `validationRules` | `array` | Custom validation rules |
|
|
214
|
+
| `autoFieldSet` | `boolean` | Auto-save to SSF on blur (default: `true`) |
|
|
215
|
+
| `isDisable` | `boolean` | Consumer-controlled disable override |
|
|
216
|
+
| `forceRefresh` | `boolean` | Toggle to force re-fetch from SSF |
|
|
217
|
+
| `extra` | `object` | Pass-through props to the underlying DS component |
|
|
218
|
+
| `rightAddon` | `ReactNode` | Custom content to the right of the input |
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## Adding a New Renderer (Extensibility)
|
|
223
|
+
|
|
224
|
+
1. Create `NewRenderer.tsx` extending `Renderer<T>`
|
|
225
|
+
2. Define props type in `renderer/types.ts`
|
|
226
|
+
3. Register in `renderer/factory/index.ts`
|
|
227
|
+
4. Add controlled props to `bll/constants.ts` (if applicable)
|
|
228
|
+
|
|
229
|
+
The factory pattern means zero changes to existing renderers or the `FieldRenderer` entry point.
|