@planning-inspectorate/dynamic-forms 3.14.0 → 3.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/package.json +1 -1
  2. package/src/components/address/question.js +14 -0
  3. package/src/components/address/question.test.js +13 -0
  4. package/src/components/date/question.js +8 -0
  5. package/src/components/date/question.test.js +17 -0
  6. package/src/components/date-period/question.js +15 -0
  7. package/src/components/date-period/question.test.js +20 -0
  8. package/src/components/date-time/question.js +15 -0
  9. package/src/components/date-time/question.test.js +11 -0
  10. package/src/components/multi-field-input/question.js +8 -0
  11. package/src/components/multi-field-input/question.test.js +7 -0
  12. package/src/questions/question.js +11 -0
  13. package/src/questions/question.test.js +7 -0
  14. package/src/validator/address-validator.js +1 -1
  15. package/src/validator/base-validator.js +1 -1
  16. package/src/validator/conditional-required-validator.js +1 -1
  17. package/src/validator/cross-question-validator.README.md +131 -0
  18. package/src/validator/cross-question-validator.js +48 -5
  19. package/src/validator/cross-question-validator.test.js +448 -11
  20. package/src/validator/date-period-validator.js +1 -5
  21. package/src/validator/date-time-validator.js +1 -5
  22. package/src/validator/date-validator.js +1 -5
  23. package/src/validator/required-validator.js +1 -5
  24. package/src/validator/unit-option-entry-validator.js +1 -5
  25. package/types/src/components/address/question.d.ts.map +1 -1
  26. package/types/src/components/date/question.d.ts.map +1 -1
  27. package/types/src/components/date-period/question.d.ts.map +1 -1
  28. package/types/src/components/date-time/question.d.ts.map +1 -1
  29. package/types/src/components/multi-field-input/question.d.ts.map +1 -1
  30. package/types/src/questions/question.d.ts +8 -0
  31. package/types/src/questions/question.d.ts.map +1 -1
  32. package/types/src/validator/address-validator.d.ts +2 -2
  33. package/types/src/validator/address-validator.d.ts.map +1 -1
  34. package/types/src/validator/base-validator.d.ts +4 -2
  35. package/types/src/validator/base-validator.d.ts.map +1 -1
  36. package/types/src/validator/conditional-required-validator.d.ts +2 -2
  37. package/types/src/validator/conditional-required-validator.d.ts.map +1 -1
  38. package/types/src/validator/cross-question-validator.d.ts +11 -4
  39. package/types/src/validator/cross-question-validator.d.ts.map +1 -1
  40. package/types/src/validator/date-period-validator.d.ts +2 -6
  41. package/types/src/validator/date-period-validator.d.ts.map +1 -1
  42. package/types/src/validator/date-time-validator.d.ts +2 -6
  43. package/types/src/validator/date-time-validator.d.ts.map +1 -1
  44. package/types/src/validator/date-validator.d.ts +2 -6
  45. package/types/src/validator/date-validator.d.ts.map +1 -1
  46. package/types/src/validator/required-validator.d.ts +2 -6
  47. package/types/src/validator/required-validator.d.ts.map +1 -1
  48. package/types/src/validator/unit-option-entry-validator.d.ts +2 -6
  49. package/types/src/validator/unit-option-entry-validator.d.ts.map +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@planning-inspectorate/dynamic-forms",
3
- "version": "3.14.0",
3
+ "version": "3.15.0",
4
4
  "description": "This package is for building GDS forms using a configuration-based approach.",
5
5
  "main": "./src/index.js",
6
6
  "type": "module",
@@ -30,6 +30,20 @@ export class AddressQuestion extends Question {
30
30
  };
31
31
  }
32
32
 
33
+ /**
34
+ * Gets the body field names used by this question in form submissions.
35
+ * @returns {string[]}
36
+ */
37
+ get bodyFieldNames() {
38
+ return [
39
+ `${this.fieldName}_addressLine1`,
40
+ `${this.fieldName}_addressLine2`,
41
+ `${this.fieldName}_townCity`,
42
+ `${this.fieldName}_county`,
43
+ `${this.fieldName}_postcode`
44
+ ];
45
+ }
46
+
33
47
  /**
34
48
  * @param {Record<string, any>} answers
35
49
  * @returns {*|string}
@@ -56,6 +56,19 @@ describe('AddressQuestion', () => {
56
56
  return { question, mockApi, req, journeyResponse };
57
57
  };
58
58
 
59
+ describe('bodyFieldNames', () => {
60
+ it('should return all address sub-field names', () => {
61
+ const { question } = setup();
62
+ assert.deepStrictEqual(question.bodyFieldNames, [
63
+ `${FIELDNAME}_addressLine1`,
64
+ `${FIELDNAME}_addressLine2`,
65
+ `${FIELDNAME}_townCity`,
66
+ `${FIELDNAME}_county`,
67
+ `${FIELDNAME}_postcode`
68
+ ]);
69
+ });
70
+ });
71
+
59
72
  describe('getDataToSave', () => {
60
73
  it('should format the data correctly to be saved', async () => {
61
74
  const { question, req } = setup();
@@ -19,6 +19,14 @@ export class DateQuestion extends Question {
19
19
  this.dateFormat = dateFormat;
20
20
  }
21
21
 
22
+ /**
23
+ * Gets the body field names used by this question in form submissions.
24
+ * @returns {string[]}
25
+ */
26
+ get bodyFieldNames() {
27
+ return [`${this.fieldName}_day`, `${this.fieldName}_month`, `${this.fieldName}_year`];
28
+ }
29
+
22
30
  /**
23
31
  * Get the data to save from the request, returns an object of answers
24
32
  * @param {import('express').Request} req
@@ -26,6 +26,23 @@ describe('DateQuestion', () => {
26
26
  });
27
27
  });
28
28
 
29
+ describe('bodyFieldNames', () => {
30
+ it('should return day, month, and year field names', () => {
31
+ const dateQuestion = new DateQuestion({
32
+ title: TITLE,
33
+ question: QUESTION,
34
+ fieldName: FIELDNAME,
35
+ hint: HINT,
36
+ validators: VALIDATORS
37
+ });
38
+ assert.deepStrictEqual(dateQuestion.bodyFieldNames, [
39
+ `${FIELDNAME}_day`,
40
+ `${FIELDNAME}_month`,
41
+ `${FIELDNAME}_year`
42
+ ]);
43
+ });
44
+ });
45
+
29
46
  describe('getDataToSave', () => {
30
47
  it('should return data correctly', async () => {
31
48
  const dateQuestion = new DateQuestion({
@@ -34,6 +34,21 @@ export class DatePeriodQuestion extends Question {
34
34
  this.endTime = endTime || { hour: 0, minute: 0, second: 0 };
35
35
  }
36
36
 
37
+ /**
38
+ * Gets the body field names used by this question in form submissions.
39
+ * @returns {string[]}
40
+ */
41
+ get bodyFieldNames() {
42
+ return [
43
+ `${this.fieldName}_start_day`,
44
+ `${this.fieldName}_start_month`,
45
+ `${this.fieldName}_start_year`,
46
+ `${this.fieldName}_end_day`,
47
+ `${this.fieldName}_end_month`,
48
+ `${this.fieldName}_end_year`
49
+ ];
50
+ }
51
+
37
52
  /**
38
53
  * Get the data to save from the request, returns an object of answers
39
54
  * @param {import('express').Request} req
@@ -30,6 +30,26 @@ describe('DatePeriodQuestion', () => {
30
30
  });
31
31
  });
32
32
 
33
+ describe('bodyFieldNames', () => {
34
+ it('should return all start and end date field names', () => {
35
+ const dateQuestion = new DatePeriodQuestion({
36
+ title: TITLE,
37
+ question: QUESTION,
38
+ fieldName: FIELDNAME,
39
+ hint: HINT,
40
+ validators: VALIDATORS
41
+ });
42
+ assert.deepStrictEqual(dateQuestion.bodyFieldNames, [
43
+ `${FIELDNAME}_start_day`,
44
+ `${FIELDNAME}_start_month`,
45
+ `${FIELDNAME}_start_year`,
46
+ `${FIELDNAME}_end_day`,
47
+ `${FIELDNAME}_end_month`,
48
+ `${FIELDNAME}_end_year`
49
+ ]);
50
+ });
51
+ });
52
+
33
53
  describe('getDataToSave', () => {
34
54
  it('should return data correctly', async () => {
35
55
  const dateQuestion = new DatePeriodQuestion({
@@ -22,6 +22,21 @@ export class DateTimeQuestion extends Question {
22
22
  this.timeFormat = timeFormat;
23
23
  }
24
24
 
25
+ /**
26
+ * Gets the body field names used by this question in form submissions.
27
+ * @returns {string[]}
28
+ */
29
+ get bodyFieldNames() {
30
+ return [
31
+ `${this.fieldName}_day`,
32
+ `${this.fieldName}_month`,
33
+ `${this.fieldName}_year`,
34
+ `${this.fieldName}_hour`,
35
+ `${this.fieldName}_minutes`,
36
+ `${this.fieldName}_period`
37
+ ];
38
+ }
39
+
25
40
  /**
26
41
  * Get the data to save from the request, returns an object of answers
27
42
  * @param {import('express').Request} req
@@ -15,6 +15,17 @@ describe('./lib/forms/custom-components/date-time/question.js', () => {
15
15
  it('should create', () => {
16
16
  assert.strictEqual(question.viewFolder, 'date-time');
17
17
  });
18
+
19
+ it('should return all date and time field names from bodyFieldNames', () => {
20
+ assert.deepStrictEqual(question.bodyFieldNames, [
21
+ 'siteVisitDate_day',
22
+ 'siteVisitDate_month',
23
+ 'siteVisitDate_year',
24
+ 'siteVisitDate_hour',
25
+ 'siteVisitDate_minutes',
26
+ 'siteVisitDate_period'
27
+ ]);
28
+ });
18
29
  });
19
30
  describe('prepQuestionForRendering', () => {
20
31
  it('should prep question for rendering when value not in payload', () => {
@@ -30,6 +30,14 @@ export class MultiFieldInputQuestion extends Question {
30
30
  }
31
31
  }
32
32
 
33
+ /**
34
+ * Gets the body field names used by this question in form submissions.
35
+ * @returns {string[]}
36
+ */
37
+ get bodyFieldNames() {
38
+ return this.inputFields.map((inputField) => inputField.fieldName);
39
+ }
40
+
33
41
  answerForViewModel(answers) {
34
42
  return this.inputFields.map((inputField) => {
35
43
  return {
@@ -51,6 +51,13 @@ describe('./src/dynamic-forms/components/multi-field-input/question.js', () => {
51
51
  }, new Error('inputFields are mandatory'));
52
52
  });
53
53
 
54
+ describe('bodyFieldNames', () => {
55
+ it('should return field names from all inputFields', () => {
56
+ const testQuestion = createMultiFieldInputQuestion();
57
+ assert.deepStrictEqual(testQuestion.bodyFieldNames, ['testField1', 'testField2']);
58
+ });
59
+ });
60
+
54
61
  describe('prepQuestionForRendering', () => {
55
62
  it('should call super and set inputFields', () => {
56
63
  const question = createMultiFieldInputQuestion();
@@ -181,6 +181,17 @@ export class Question {
181
181
  this._isInManageListSection = value;
182
182
  }
183
183
 
184
+ /**
185
+ * Gets the body field names used by this question in form submissions.
186
+ * Returns the field names that should be present in req.body when this question is submitted.
187
+ * Subclasses should override this for questions with multiple or differently-named body fields.
188
+ *
189
+ * @returns {string[]}
190
+ */
191
+ get bodyFieldNames() {
192
+ return [this.fieldName];
193
+ }
194
+
184
195
  /**
185
196
  * gets the view model for this question
186
197
  *
@@ -135,6 +135,13 @@ describe('./src/dynamic-forms/question.js', () => {
135
135
  });
136
136
  });
137
137
 
138
+ describe('bodyFieldNames', () => {
139
+ it('should return an array containing the fieldName by default', () => {
140
+ const question = getTestQuestion();
141
+ assert.deepStrictEqual(question.bodyFieldNames, [FIELDNAME]);
142
+ });
143
+ });
144
+
138
145
  describe('prepQuestionForRendering', () => {
139
146
  it('should prepQuestionForRendering', () => {
140
147
  const question = getTestQuestion();
@@ -41,7 +41,7 @@ export class AddressValidator extends BaseValidator {
41
41
 
42
42
  /**
43
43
  * validates response body using questionObj fieldname
44
- * @param {Question} questionObj
44
+ * @param {import('../questions/question.js').Question} questionObj
45
45
  */
46
46
  validate(questionObj) {
47
47
  const fieldName = questionObj.fieldName;
@@ -18,7 +18,7 @@ export class BaseValidator {
18
18
  * Validates response body against field validators.
19
19
  * Subclasses must override this method.
20
20
  * @abstract
21
- * @param {import('../questions/question-props.js').QuestionProps} questionObj - The question object containing the fieldName to validate.
21
+ * @param {{fieldName: string}} questionObj - The question object containing the fieldName to validate.
22
22
  * @param {import('../journey/journey-response.js').JourneyResponse} [journeyResponse] - The current journey response (optional).
23
23
  * @returns {import('express-validator').ValidationChain | import('express-validator').ValidationChain[]}
24
24
  */
@@ -31,7 +31,7 @@ export class ConditionalRequiredValidator extends BaseValidator {
31
31
 
32
32
  /**
33
33
  * validates the response body, checking the questionObj's fieldname
34
- * @param {Question} questionObj
34
+ * @param {import('../questions/options-question.js').OptionsQuestion} questionObj
35
35
  */
36
36
  validate(questionObj) {
37
37
  return questionObj.options.reduce((schema, option) => {
@@ -0,0 +1,131 @@
1
+ # Cross-question validator
2
+
3
+ The `CrossQuestionValidator` enables validation of a question's answer against another question's answer using a custom validation function. This is useful for scenarios where the validity of one answer depends on another answer.
4
+
5
+ ## Constructor options
6
+
7
+ | Parameter | Type | Default | Description |
8
+ | ------------------------- | ---------------------------------------------- | ------------ | ---------------------------------------------------------------------------------------------- |
9
+ | `dependencyFieldName` | `string` | **required** | The field name of the other question to compare against |
10
+ | `validationFunction` | `(currentAnswer, dependencyAnswer) => boolean` | **required** | Function that returns `true` if valid, or throws an `Error` with a specific message if invalid |
11
+ | `useBodyValues` | `boolean` | `false` | Use `req.body` values for **both** current and dependency answers |
12
+ | `useBodyValuesForCurrent` | `boolean` | `false` | Use `req.body` value for current answer only (dependency still uses JourneyResponse) |
13
+
14
+ ## Use cases
15
+
16
+ ### 1. Validation between separate manage list questions (JourneyResponse data)
17
+
18
+ When validating between questions where both answers are already saved to the JourneyResponse (e.g., in a manage list journey), use the default settings.
19
+
20
+ ```js
21
+ import { CrossQuestionValidator } from '@planning-inspectorate/dynamic-forms';
22
+
23
+ const startDateQuestion = {
24
+ type: COMPONENT_TYPES.DATE,
25
+ fieldName: 'startDate',
26
+ question: 'What is the start date?'
27
+ // ... other config
28
+ };
29
+
30
+ const endDateQuestion = {
31
+ type: COMPONENT_TYPES.DATE,
32
+ fieldName: 'endDate',
33
+ question: 'What is the end date?',
34
+ validators: [
35
+ new CrossQuestionValidator({
36
+ dependencyFieldName: 'startDate',
37
+ validationFunction: (endDate, startDate) => {
38
+ if (!endDate || !startDate) return true;
39
+ if (new Date(endDate) < new Date(startDate)) {
40
+ throw new Error('End date must be on or after the start date');
41
+ }
42
+ return true;
43
+ }
44
+ })
45
+ ]
46
+ };
47
+ ```
48
+
49
+ **When to use:** Both questions are on separate pages and their answers are saved to the JourneyResponse before validation runs.
50
+
51
+ ### 2. Validation between regular questions (body data with formatting)
52
+
53
+ When the current question's data needs to be extracted from `req.body` and formatted (e.g., date fields that come as separate day/month/year inputs), use `useBodyValuesForCurrent: true`.
54
+
55
+ The validator will automatically use the question's `getDataToSave()` method to format the data correctly.
56
+
57
+ ```js
58
+ const projectEndDateQuestion = {
59
+ type: COMPONENT_TYPES.DATE,
60
+ fieldName: 'projectEndDate',
61
+ question: 'When does the project end?',
62
+ validators: [
63
+ new CrossQuestionValidator({
64
+ dependencyFieldName: 'projectStartDate', // Already saved in JourneyResponse
65
+ useBodyValuesForCurrent: true, // Current answer from body, formatted via getDataToSave()
66
+ validationFunction: (endDate, startDate) => {
67
+ if (!endDate || !startDate) return true;
68
+ if (endDate < new Date(startDate)) {
69
+ throw new Error('Project end date must be on or after the project start date');
70
+ }
71
+ return true;
72
+ }
73
+ })
74
+ ]
75
+ };
76
+ ```
77
+
78
+ **When to use:** The current question's answer is in `req.body` (not yet saved), but the dependency answer is already saved to the JourneyResponse. Works automatically with date questions, date-period questions, and other question types that have a `getDataToSave()` method.
79
+
80
+ ### 3. Validation between fields in a multi-field question (both from body)
81
+
82
+ When validating between multiple fields on the same page (e.g., a `MultiFieldInputQuestion`), both current and reference values come from `req.body`. Use `useBodyValues: true`.
83
+
84
+ ```js
85
+ const siteAreaQuestion = {
86
+ type: COMPONENT_TYPES.MULTI_FIELD_INPUT,
87
+ fieldName: 'siteArea',
88
+ question: 'What is the area of the site?',
89
+ inputFields: [
90
+ { fieldName: 'siteAreaHectares', label: 'Site area in hectares (optional)' },
91
+ { fieldName: 'siteAreaSquareMetres', label: 'Site area in square metres (optional)' }
92
+ ],
93
+ validators: [
94
+ // Note: this is a custom validator not provided by default
95
+ new MultiFieldInputValidator({
96
+ fields: [
97
+ {
98
+ fieldName: 'siteAreaHectares',
99
+ validators: [
100
+ new CrossQuestionValidator({
101
+ dependencyFieldName: 'siteAreaSquareMetres',
102
+ useBodyValues: true,
103
+ validationFunction: (hectares, squareMetres) => {
104
+ if (hectares?.trim() && squareMetres?.trim()) {
105
+ throw new Error('Enter the site area in either hectares or square metres, not both');
106
+ }
107
+ return true;
108
+ }
109
+ })
110
+ ]
111
+ }
112
+ ]
113
+ })
114
+ ]
115
+ };
116
+ ```
117
+
118
+ **When to use:** Multiple fields are on the same page and both values need to be read from `req.body` before either is saved.
119
+
120
+ ## How data sources are determined
121
+
122
+ | `useBodyValues` | `useBodyValuesForCurrent` | Current Answer Source | Dependency Answer Source |
123
+ | --------------- | ------------------------- | ---------------------------- | ------------------------ |
124
+ | `false` | `false` | JourneyResponse | JourneyResponse |
125
+ | `false` | `true` | Body (via `getDataToSave()`) | JourneyResponse |
126
+ | `true` | (ignored) | Body (via `getDataToSave()`) | Body (raw) |
127
+
128
+ ## Notes
129
+
130
+ - When `useBodyValuesForCurrent` is `true`, the validator uses the question's `getDataToSave()` method to format body data. This automatically handles complex field types like dates (`fieldName_day`, `fieldName_month`, `fieldName_year`).
131
+ - The validator binds to the appropriate body field using the question's `bodyFieldNames` getter. For simple questions this returns `[fieldName]`, but complex question types override it (e.g., `DateQuestion` returns `[fieldName_day, fieldName_month, fieldName_year]`). Custom question components can define their own `bodyFieldNames` getter to ensure proper validation binding.
@@ -11,16 +11,24 @@ export class CrossQuestionValidator extends BaseValidator {
11
11
  validationFunction;
12
12
  /** @type {boolean} */
13
13
  useBodyValues;
14
+ /** @type {boolean} */
15
+ useBodyValuesForCurrent;
14
16
 
15
17
  /**
16
18
  * @param {Object} params
17
19
  * @param {string} params.dependencyFieldName - The field name of the other question to compare against.
18
20
  * @param {((currentAnswer: unknown, dependencyAnswer: unknown) => boolean)| null} params.validationFunction - A function that takes the current question's answer and the other question's answer and returns true if valid, false otherwise.
19
- * @param {boolean} [params.useBodyValues=false] - Whether to use req.body values for validation (default: false). Usually CrossQuestionValidator will be for validating across saved session answers, but if used between multiple fields on one question, it will need to use the body.
21
+ * @param {boolean} [params.useBodyValues=false] - Whether to use req.body values for validation (default: false). Usually CrossQuestionValidator will be for validating across saved JourneyResponse answers, but if used between multiple fields on one question, it will need to use the body.
22
+ * @param {boolean} [params.useBodyValuesForCurrent=false] - Whether to use req.body values for the current question's answer (default: false). This may help with questions where data is not saved to the JourneyResponse on POST (e.g. non-multiple entities questions).
20
23
  * @throws {Error} If validationFunction is not provided or is not a function.
21
24
  * @throws {Error} If dependencyFieldName is not provided
22
25
  */
23
- constructor({ dependencyFieldName, validationFunction, useBodyValues = false } = {}) {
26
+ constructor({
27
+ dependencyFieldName,
28
+ validationFunction,
29
+ useBodyValues = false,
30
+ useBodyValuesForCurrent = false
31
+ } = {}) {
24
32
  super();
25
33
 
26
34
  if (!dependencyFieldName) {
@@ -33,24 +41,59 @@ export class CrossQuestionValidator extends BaseValidator {
33
41
  this.dependencyFieldName = dependencyFieldName;
34
42
  this.validationFunction = validationFunction;
35
43
  this.useBodyValues = useBodyValues;
44
+ this.useBodyValuesForCurrent = useBodyValues || useBodyValuesForCurrent;
45
+ }
46
+
47
+ /**
48
+ * Gets the body field name to bind validation to.
49
+ * Uses the question's bodyFieldNames getter to determine which body field
50
+ * express-validator binds to for triggering validation.
51
+ * @param {import('../questions/question.js').Question} questionObj
52
+ * @returns {string}
53
+ */
54
+ #getBodyFieldName(questionObj) {
55
+ // Use the first body field name from the question's bodyFieldNames getter
56
+ // This allows questions to define their own body field names, supporting
57
+ // custom components and complex field structures (e.g. date, date-time, address)
58
+ return questionObj.bodyFieldNames?.[0] ?? questionObj.fieldName;
36
59
  }
37
60
 
38
61
  /**
39
62
  * Validates response body against individual field validators.
40
- * @param {import('../questions/question-props.js').QuestionProps} questionObj - The question object containing the fieldName to validate.
63
+ * Uses the question's getDataToSave method to extract and format body values when useBodyValuesForCurrent is true,
64
+ * allowing generic handling of any question type without needing to know its internal structure.
65
+ * @param {import('../questions/question.js').Question} questionObj - The question object containing the fieldName to validate.
41
66
  * @param {import('../journey/journey-response.js').JourneyResponse} [journeyResponse={}] - The current journey response, used to access saved answers for validation.
42
67
  * @returns {import('express-validator').ValidationChain[]}
43
68
  */
44
69
  validate(questionObj, journeyResponse = {}) {
45
70
  const fieldName = questionObj.fieldName;
71
+ const bodyFieldName = this.#getBodyFieldName(questionObj);
46
72
 
47
73
  return [
48
- body(fieldName).custom((value, { req }) => {
74
+ body(bodyFieldName).custom(async (value, { req }) => {
49
75
  const answers = journeyResponse?.answers || {};
50
- const currentAnswer = this.useBodyValues ? value : answers[fieldName];
76
+
77
+ let currentAnswer;
78
+ if (this.useBodyValuesForCurrent) {
79
+ // Use the question's own getDataToSave to format body data generically
80
+ if (typeof questionObj.getDataToSave === 'function') {
81
+ const { answers: formattedAnswers } = await questionObj.getDataToSave(req, journeyResponse);
82
+ // Use the formatted answer if available, otherwise return all formatted answers
83
+ // (handles MultiFieldInputQuestion which returns answers keyed by input field names)
84
+ currentAnswer = fieldName in formattedAnswers ? formattedAnswers[fieldName] : formattedAnswers;
85
+ } else {
86
+ // Fallback to raw body value if getDataToSave is not available
87
+ currentAnswer = req.body[fieldName];
88
+ }
89
+ } else {
90
+ currentAnswer = answers[fieldName];
91
+ }
92
+
51
93
  const dependencyAnswer = this.useBodyValues
52
94
  ? req.body[this.dependencyFieldName]
53
95
  : answers[this.dependencyFieldName];
96
+
54
97
  return (
55
98
  this.validationFunction(currentAnswer, dependencyAnswer) ||
56
99
  // Fallback if validation fails without throwing an error