@defra/forms-engine-plugin 4.0.10 → 4.0.12
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/.server/server/forms/register-as-a-unicorn-breeder.yaml +2 -2
- package/.server/server/plugins/engine/components/EastingNorthingField.js +4 -24
- package/.server/server/plugins/engine/components/EastingNorthingField.js.map +1 -1
- package/.server/server/plugins/engine/components/LatLongField.js +8 -31
- package/.server/server/plugins/engine/components/LatLongField.js.map +1 -1
- package/.server/server/plugins/engine/components/LocationFieldBase.d.ts +1 -3
- package/.server/server/plugins/engine/components/LocationFieldBase.js +1 -8
- package/.server/server/plugins/engine/components/LocationFieldBase.js.map +1 -1
- package/.server/server/plugins/engine/components/NationalGridFieldNumberField.d.ts +0 -2
- package/.server/server/plugins/engine/components/NationalGridFieldNumberField.js +7 -18
- package/.server/server/plugins/engine/components/NationalGridFieldNumberField.js.map +1 -1
- package/.server/server/plugins/engine/components/NumberField.d.ts +0 -18
- package/.server/server/plugins/engine/components/NumberField.js +2 -103
- package/.server/server/plugins/engine/components/NumberField.js.map +1 -1
- package/.server/server/plugins/engine/components/OsGridRefField.d.ts +0 -2
- package/.server/server/plugins/engine/components/OsGridRefField.js +4 -32
- package/.server/server/plugins/engine/components/OsGridRefField.js.map +1 -1
- package/.server/server/plugins/engine/pageControllers/helpers/pages.d.ts +1 -2
- package/.server/server/plugins/engine/pageControllers/helpers/pages.js +1 -11
- package/.server/server/plugins/engine/pageControllers/helpers/pages.js.map +1 -1
- package/package.json +1 -1
- package/src/server/forms/register-as-a-unicorn-breeder.yaml +2 -2
- package/src/server/plugins/engine/components/EastingNorthingField.test.ts +0 -14
- package/src/server/plugins/engine/components/EastingNorthingField.ts +4 -24
- package/src/server/plugins/engine/components/LatLongField.test.ts +4 -24
- package/src/server/plugins/engine/components/LatLongField.ts +8 -33
- package/src/server/plugins/engine/components/LocationFieldBase.ts +1 -15
- package/src/server/plugins/engine/components/NationalGridFieldNumberField.test.ts +22 -8
- package/src/server/plugins/engine/components/NationalGridFieldNumberField.ts +9 -20
- package/src/server/plugins/engine/components/NumberField.test.ts +12 -504
- package/src/server/plugins/engine/components/NumberField.ts +4 -133
- package/src/server/plugins/engine/components/OsGridRefField.test.ts +11 -33
- package/src/server/plugins/engine/components/OsGridRefField.ts +5 -38
- package/src/server/plugins/engine/pageControllers/helpers/helpers.test.ts +4 -24
- package/src/server/plugins/engine/pageControllers/helpers/pages.ts +0 -15
|
@@ -203,7 +203,7 @@ pages:
|
|
|
203
203
|
path: '/how-many-members-of-staff-will-look-after-the-unicorns'
|
|
204
204
|
section: susaYr
|
|
205
205
|
next:
|
|
206
|
-
- path: '/
|
|
206
|
+
- path: '/declaration'
|
|
207
207
|
components:
|
|
208
208
|
- name: zhJMaM
|
|
209
209
|
options:
|
|
@@ -219,7 +219,7 @@ pages:
|
|
|
219
219
|
controller: FileUploadPageController
|
|
220
220
|
section: Regnsa
|
|
221
221
|
next:
|
|
222
|
-
- path: '/
|
|
222
|
+
- path: '/how-many-unicorns-do-you-expect-to-breed-each-year'
|
|
223
223
|
components:
|
|
224
224
|
- name: dLzALM
|
|
225
225
|
title: Documents
|
|
@@ -11,18 +11,6 @@ const DEFAULT_EASTING_MIN = 0;
|
|
|
11
11
|
const DEFAULT_EASTING_MAX = 700000;
|
|
12
12
|
const DEFAULT_NORTHING_MIN = 0;
|
|
13
13
|
const DEFAULT_NORTHING_MAX = 1300000;
|
|
14
|
-
|
|
15
|
-
// Easting length constraints (integer values only, no decimals)
|
|
16
|
-
// Min: 1 char for values like "0" or single digit values
|
|
17
|
-
// Max: 6 chars for values up to 700000 (British National Grid easting limit)
|
|
18
|
-
const EASTING_MIN_LENGTH = 1;
|
|
19
|
-
const EASTING_MAX_LENGTH = 6;
|
|
20
|
-
|
|
21
|
-
// Northing length constraints (integer values only, no decimals)
|
|
22
|
-
// Min: 1 char for values like "0" or single digit values
|
|
23
|
-
// Max: 7 chars for values up to 1300000 (British National Grid northing limit)
|
|
24
|
-
const NORTHING_MIN_LENGTH = 1;
|
|
25
|
-
const NORTHING_MAX_LENGTH = 7;
|
|
26
14
|
export class EastingNorthingField extends FormComponent {
|
|
27
15
|
constructor(def, props) {
|
|
28
16
|
super(def, props);
|
|
@@ -43,9 +31,7 @@ export class EastingNorthingField extends FormComponent {
|
|
|
43
31
|
'number.max': `{{#label}} for ${this.title} must be between ${eastingMin} and {{#limit}}`,
|
|
44
32
|
'number.precision': `{{#label}} for ${this.title} must be between 1 and 6 digits`,
|
|
45
33
|
'number.integer': `{{#label}} for ${this.title} must be between 1 and 6 digits`,
|
|
46
|
-
'number.unsafe': `{{#label}} for ${this.title} must be between 1 and 6 digits
|
|
47
|
-
'number.minLength': `{{#label}} for ${this.title} must be between 1 and 6 digits`,
|
|
48
|
-
'number.maxLength': `{{#label}} for ${this.title} must be between 1 and 6 digits`
|
|
34
|
+
'number.unsafe': `{{#label}} for ${this.title} must be between 1 and 6 digits`
|
|
49
35
|
});
|
|
50
36
|
const northingValidationMessages = convertToLanguageMessages({
|
|
51
37
|
'any.required': messageTemplate.objectMissing,
|
|
@@ -54,9 +40,7 @@ export class EastingNorthingField extends FormComponent {
|
|
|
54
40
|
'number.max': `{{#label}} for ${this.title} must be between ${northingMin} and {{#limit}}`,
|
|
55
41
|
'number.precision': `{{#label}} for ${this.title} must be between 1 and 7 digits`,
|
|
56
42
|
'number.integer': `{{#label}} for ${this.title} must be between 1 and 7 digits`,
|
|
57
|
-
'number.unsafe': `{{#label}} for ${this.title} must be between 1 and 7 digits
|
|
58
|
-
'number.minLength': `{{#label}} for ${this.title} must be between 1 and 7 digits`,
|
|
59
|
-
'number.maxLength': `{{#label}} for ${this.title} must be between 1 and 7 digits`
|
|
43
|
+
'number.unsafe': `{{#label}} for ${this.title} must be between 1 and 7 digits`
|
|
60
44
|
});
|
|
61
45
|
this.collection = new ComponentCollection([{
|
|
62
46
|
type: ComponentType.NumberField,
|
|
@@ -65,9 +49,7 @@ export class EastingNorthingField extends FormComponent {
|
|
|
65
49
|
schema: {
|
|
66
50
|
min: eastingMin,
|
|
67
51
|
max: eastingMax,
|
|
68
|
-
precision: 0
|
|
69
|
-
minLength: EASTING_MIN_LENGTH,
|
|
70
|
-
maxLength: EASTING_MAX_LENGTH
|
|
52
|
+
precision: 0
|
|
71
53
|
},
|
|
72
54
|
options: {
|
|
73
55
|
required: isRequired,
|
|
@@ -82,9 +64,7 @@ export class EastingNorthingField extends FormComponent {
|
|
|
82
64
|
schema: {
|
|
83
65
|
min: northingMin,
|
|
84
66
|
max: northingMax,
|
|
85
|
-
precision: 0
|
|
86
|
-
minLength: NORTHING_MIN_LENGTH,
|
|
87
|
-
maxLength: NORTHING_MAX_LENGTH
|
|
67
|
+
precision: 0
|
|
88
68
|
},
|
|
89
69
|
options: {
|
|
90
70
|
required: isRequired,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EastingNorthingField.js","names":["ComponentType","ComponentCollection","FormComponent","isFormState","createLocationFieldValidator","getLocationFieldViewModel","NumberField","messageTemplate","convertToLanguageMessages","DEFAULT_EASTING_MIN","DEFAULT_EASTING_MAX","DEFAULT_NORTHING_MIN","DEFAULT_NORTHING_MAX","EASTING_MIN_LENGTH","EASTING_MAX_LENGTH","NORTHING_MIN_LENGTH","NORTHING_MAX_LENGTH","EastingNorthingField","constructor","def","props","name","options","schema","isRequired","required","eastingMin","easting","min","eastingMax","max","northingMin","northing","northingMax","customValidationMessages","objectMissing","title","northingValidationMessages","collection","type","precision","minLength","maxLength","optionalText","classes","parent","custom","getValidatorEastingNorthing","peers","formSchema","stateSchema","getFormValueFromState","state","value","isEastingNorthing","undefined","getDisplayStringFromFormValue","getDisplayStringFromState","getContextValueFromFormValue","getContextValueFromState","getViewModel","payload","errors","viewModel","isState","getAllPossibleErrors","baseErrors","template","advancedSettingsErrors","isNumber","component"],"sources":["../../../../../src/server/plugins/engine/components/EastingNorthingField.ts"],"sourcesContent":["import {\n ComponentType,\n type EastingNorthingFieldComponent\n} from '@defra/forms-model'\nimport { type LanguageMessages, type ObjectSchema } from 'joi'\n\nimport { ComponentCollection } from '~/src/server/plugins/engine/components/ComponentCollection.js'\nimport {\n FormComponent,\n isFormState\n} from '~/src/server/plugins/engine/components/FormComponent.js'\nimport {\n createLocationFieldValidator,\n getLocationFieldViewModel\n} from '~/src/server/plugins/engine/components/LocationFieldHelpers.js'\nimport { NumberField } from '~/src/server/plugins/engine/components/NumberField.js'\nimport { type EastingNorthingState } from '~/src/server/plugins/engine/components/types.js'\nimport { messageTemplate } from '~/src/server/plugins/engine/pageControllers/validationOptions.js'\nimport {\n type ErrorMessageTemplateList,\n type FormPayload,\n type FormState,\n type FormStateValue,\n type FormSubmissionError,\n type FormSubmissionState\n} from '~/src/server/plugins/engine/types.js'\nimport { convertToLanguageMessages } from '~/src/server/utils/type-utils.js'\n\n// British National Grid coordinate limits\nconst DEFAULT_EASTING_MIN = 0\nconst DEFAULT_EASTING_MAX = 700000\nconst DEFAULT_NORTHING_MIN = 0\nconst DEFAULT_NORTHING_MAX = 1300000\n\n// Easting length constraints (integer values only, no decimals)\n// Min: 1 char for values like \"0\" or single digit values\n// Max: 6 chars for values up to 700000 (British National Grid easting limit)\nconst EASTING_MIN_LENGTH = 1\nconst EASTING_MAX_LENGTH = 6\n\n// Northing length constraints (integer values only, no decimals)\n// Min: 1 char for values like \"0\" or single digit values\n// Max: 7 chars for values up to 1300000 (British National Grid northing limit)\nconst NORTHING_MIN_LENGTH = 1\nconst NORTHING_MAX_LENGTH = 7\n\nexport class EastingNorthingField extends FormComponent {\n declare options: EastingNorthingFieldComponent['options']\n declare formSchema: ObjectSchema<FormPayload>\n declare stateSchema: ObjectSchema<FormState>\n declare collection: ComponentCollection\n\n constructor(\n def: EastingNorthingFieldComponent,\n props: ConstructorParameters<typeof FormComponent>[1]\n ) {\n super(def, props)\n\n const { name, options, schema } = def\n\n const isRequired = options.required !== false\n\n const eastingMin = schema?.easting?.min ?? DEFAULT_EASTING_MIN\n const eastingMax = schema?.easting?.max ?? DEFAULT_EASTING_MAX\n const northingMin = schema?.northing?.min ?? DEFAULT_NORTHING_MIN\n const northingMax = schema?.northing?.max ?? DEFAULT_NORTHING_MAX\n\n const customValidationMessages: LanguageMessages =\n convertToLanguageMessages({\n 'any.required': messageTemplate.objectMissing,\n 'number.base': messageTemplate.objectMissing,\n 'number.min': `{{#label}} for ${this.title} must be between {{#limit}} and ${eastingMax}`,\n 'number.max': `{{#label}} for ${this.title} must be between ${eastingMin} and {{#limit}}`,\n 'number.precision': `{{#label}} for ${this.title} must be between 1 and 6 digits`,\n 'number.integer': `{{#label}} for ${this.title} must be between 1 and 6 digits`,\n 'number.unsafe': `{{#label}} for ${this.title} must be between 1 and 6 digits`,\n 'number.minLength': `{{#label}} for ${this.title} must be between 1 and 6 digits`,\n 'number.maxLength': `{{#label}} for ${this.title} must be between 1 and 6 digits`\n })\n\n const northingValidationMessages: LanguageMessages =\n convertToLanguageMessages({\n 'any.required': messageTemplate.objectMissing,\n 'number.base': messageTemplate.objectMissing,\n 'number.min': `{{#label}} for ${this.title} must be between {{#limit}} and ${northingMax}`,\n 'number.max': `{{#label}} for ${this.title} must be between ${northingMin} and {{#limit}}`,\n 'number.precision': `{{#label}} for ${this.title} must be between 1 and 7 digits`,\n 'number.integer': `{{#label}} for ${this.title} must be between 1 and 7 digits`,\n 'number.unsafe': `{{#label}} for ${this.title} must be between 1 and 7 digits`,\n 'number.minLength': `{{#label}} for ${this.title} must be between 1 and 7 digits`,\n 'number.maxLength': `{{#label}} for ${this.title} must be between 1 and 7 digits`\n })\n\n this.collection = new ComponentCollection(\n [\n {\n type: ComponentType.NumberField,\n name: `${name}__easting`,\n title: 'Easting',\n schema: {\n min: eastingMin,\n max: eastingMax,\n precision: 0,\n minLength: EASTING_MIN_LENGTH,\n maxLength: EASTING_MAX_LENGTH\n },\n options: {\n required: isRequired,\n optionalText: true,\n classes: 'govuk-input--width-10',\n customValidationMessages\n }\n },\n {\n type: ComponentType.NumberField,\n name: `${name}__northing`,\n title: 'Northing',\n schema: {\n min: northingMin,\n max: northingMax,\n precision: 0,\n minLength: NORTHING_MIN_LENGTH,\n maxLength: NORTHING_MAX_LENGTH\n },\n options: {\n required: isRequired,\n optionalText: true,\n classes: 'govuk-input--width-10',\n customValidationMessages: northingValidationMessages\n }\n }\n ],\n { ...props, parent: this },\n {\n custom: getValidatorEastingNorthing(this),\n peers: [`${name}__easting`, `${name}__northing`]\n }\n )\n\n this.options = options\n this.formSchema = this.collection.formSchema\n this.stateSchema = this.collection.stateSchema\n }\n\n getFormValueFromState(state: FormSubmissionState) {\n const value = super.getFormValueFromState(state)\n return EastingNorthingField.isEastingNorthing(value) ? value : undefined\n }\n\n getDisplayStringFromFormValue(\n value: EastingNorthingState | undefined\n ): string {\n if (!value) {\n return ''\n }\n\n // CYA page format: <<northingvalue, eastingvalue>>\n return `${value.northing}, ${value.easting}`\n }\n\n getDisplayStringFromState(state: FormSubmissionState) {\n const value = this.getFormValueFromState(state)\n\n return this.getDisplayStringFromFormValue(value)\n }\n\n getContextValueFromFormValue(\n value: EastingNorthingState | undefined\n ): string | null {\n if (!value) {\n return null\n }\n\n // Output format: Northing: <<entry>>\\nEasting: <<entry>>\n return `Northing: ${value.northing}\\nEasting: ${value.easting}`\n }\n\n getContextValueFromState(state: FormSubmissionState) {\n const value = this.getFormValueFromState(state)\n\n return this.getContextValueFromFormValue(value)\n }\n\n getViewModel(payload: FormPayload, errors?: FormSubmissionError[]) {\n const viewModel = super.getViewModel(payload, errors)\n return getLocationFieldViewModel(this, viewModel, payload, errors)\n }\n\n isState(value?: FormStateValue | FormState) {\n return EastingNorthingField.isEastingNorthing(value)\n }\n\n /**\n * For error preview page that shows all possible errors on a component\n */\n getAllPossibleErrors(): ErrorMessageTemplateList {\n return EastingNorthingField.getAllPossibleErrors()\n }\n\n /**\n * Static version of getAllPossibleErrors that doesn't require a component instance.\n */\n static getAllPossibleErrors(): ErrorMessageTemplateList {\n return {\n baseErrors: [\n { type: 'required', template: messageTemplate.required },\n {\n type: 'eastingFormat',\n template:\n 'Easting for [short description] must be between 1 and 6 digits'\n },\n {\n type: 'northingFormat',\n template:\n 'Northing for [short description] must be between 1 and 7 digits'\n }\n ],\n advancedSettingsErrors: [\n {\n type: 'eastingMin',\n template: `Easting for [short description] must be between 0 and 700000`\n },\n {\n type: 'eastingMax',\n template: `Easting for [short description] must be between 0 and 700000`\n },\n {\n type: 'northingMin',\n template: `Northing for [short description] must be between ${DEFAULT_NORTHING_MIN} and ${DEFAULT_NORTHING_MAX}`\n },\n {\n type: 'northingMax',\n template: `Northing for [short description] must be between ${DEFAULT_NORTHING_MIN} and ${DEFAULT_NORTHING_MAX}`\n }\n ]\n }\n }\n\n static isEastingNorthing(\n value?: FormStateValue | FormState\n ): value is EastingNorthingState {\n return (\n isFormState(value) &&\n NumberField.isNumber(value.easting) &&\n NumberField.isNumber(value.northing)\n )\n }\n}\n\nexport function getValidatorEastingNorthing(component: EastingNorthingField) {\n return createLocationFieldValidator(component)\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"EastingNorthingField.js","names":["ComponentType","ComponentCollection","FormComponent","isFormState","createLocationFieldValidator","getLocationFieldViewModel","NumberField","messageTemplate","convertToLanguageMessages","DEFAULT_EASTING_MIN","DEFAULT_EASTING_MAX","DEFAULT_NORTHING_MIN","DEFAULT_NORTHING_MAX","EastingNorthingField","constructor","def","props","name","options","schema","isRequired","required","eastingMin","easting","min","eastingMax","max","northingMin","northing","northingMax","customValidationMessages","objectMissing","title","northingValidationMessages","collection","type","precision","optionalText","classes","parent","custom","getValidatorEastingNorthing","peers","formSchema","stateSchema","getFormValueFromState","state","value","isEastingNorthing","undefined","getDisplayStringFromFormValue","getDisplayStringFromState","getContextValueFromFormValue","getContextValueFromState","getViewModel","payload","errors","viewModel","isState","getAllPossibleErrors","baseErrors","template","advancedSettingsErrors","isNumber","component"],"sources":["../../../../../src/server/plugins/engine/components/EastingNorthingField.ts"],"sourcesContent":["import {\n ComponentType,\n type EastingNorthingFieldComponent\n} from '@defra/forms-model'\nimport { type LanguageMessages, type ObjectSchema } from 'joi'\n\nimport { ComponentCollection } from '~/src/server/plugins/engine/components/ComponentCollection.js'\nimport {\n FormComponent,\n isFormState\n} from '~/src/server/plugins/engine/components/FormComponent.js'\nimport {\n createLocationFieldValidator,\n getLocationFieldViewModel\n} from '~/src/server/plugins/engine/components/LocationFieldHelpers.js'\nimport { NumberField } from '~/src/server/plugins/engine/components/NumberField.js'\nimport { type EastingNorthingState } from '~/src/server/plugins/engine/components/types.js'\nimport { messageTemplate } from '~/src/server/plugins/engine/pageControllers/validationOptions.js'\nimport {\n type ErrorMessageTemplateList,\n type FormPayload,\n type FormState,\n type FormStateValue,\n type FormSubmissionError,\n type FormSubmissionState\n} from '~/src/server/plugins/engine/types.js'\nimport { convertToLanguageMessages } from '~/src/server/utils/type-utils.js'\n\n// British National Grid coordinate limits\nconst DEFAULT_EASTING_MIN = 0\nconst DEFAULT_EASTING_MAX = 700000\nconst DEFAULT_NORTHING_MIN = 0\nconst DEFAULT_NORTHING_MAX = 1300000\n\nexport class EastingNorthingField extends FormComponent {\n declare options: EastingNorthingFieldComponent['options']\n declare formSchema: ObjectSchema<FormPayload>\n declare stateSchema: ObjectSchema<FormState>\n declare collection: ComponentCollection\n\n constructor(\n def: EastingNorthingFieldComponent,\n props: ConstructorParameters<typeof FormComponent>[1]\n ) {\n super(def, props)\n\n const { name, options, schema } = def\n\n const isRequired = options.required !== false\n\n const eastingMin = schema?.easting?.min ?? DEFAULT_EASTING_MIN\n const eastingMax = schema?.easting?.max ?? DEFAULT_EASTING_MAX\n const northingMin = schema?.northing?.min ?? DEFAULT_NORTHING_MIN\n const northingMax = schema?.northing?.max ?? DEFAULT_NORTHING_MAX\n\n const customValidationMessages: LanguageMessages =\n convertToLanguageMessages({\n 'any.required': messageTemplate.objectMissing,\n 'number.base': messageTemplate.objectMissing,\n 'number.min': `{{#label}} for ${this.title} must be between {{#limit}} and ${eastingMax}`,\n 'number.max': `{{#label}} for ${this.title} must be between ${eastingMin} and {{#limit}}`,\n 'number.precision': `{{#label}} for ${this.title} must be between 1 and 6 digits`,\n 'number.integer': `{{#label}} for ${this.title} must be between 1 and 6 digits`,\n 'number.unsafe': `{{#label}} for ${this.title} must be between 1 and 6 digits`\n })\n\n const northingValidationMessages: LanguageMessages =\n convertToLanguageMessages({\n 'any.required': messageTemplate.objectMissing,\n 'number.base': messageTemplate.objectMissing,\n 'number.min': `{{#label}} for ${this.title} must be between {{#limit}} and ${northingMax}`,\n 'number.max': `{{#label}} for ${this.title} must be between ${northingMin} and {{#limit}}`,\n 'number.precision': `{{#label}} for ${this.title} must be between 1 and 7 digits`,\n 'number.integer': `{{#label}} for ${this.title} must be between 1 and 7 digits`,\n 'number.unsafe': `{{#label}} for ${this.title} must be between 1 and 7 digits`\n })\n\n this.collection = new ComponentCollection(\n [\n {\n type: ComponentType.NumberField,\n name: `${name}__easting`,\n title: 'Easting',\n schema: {\n min: eastingMin,\n max: eastingMax,\n precision: 0\n },\n options: {\n required: isRequired,\n optionalText: true,\n classes: 'govuk-input--width-10',\n customValidationMessages\n }\n },\n {\n type: ComponentType.NumberField,\n name: `${name}__northing`,\n title: 'Northing',\n schema: {\n min: northingMin,\n max: northingMax,\n precision: 0\n },\n options: {\n required: isRequired,\n optionalText: true,\n classes: 'govuk-input--width-10',\n customValidationMessages: northingValidationMessages\n }\n }\n ],\n { ...props, parent: this },\n {\n custom: getValidatorEastingNorthing(this),\n peers: [`${name}__easting`, `${name}__northing`]\n }\n )\n\n this.options = options\n this.formSchema = this.collection.formSchema\n this.stateSchema = this.collection.stateSchema\n }\n\n getFormValueFromState(state: FormSubmissionState) {\n const value = super.getFormValueFromState(state)\n return EastingNorthingField.isEastingNorthing(value) ? value : undefined\n }\n\n getDisplayStringFromFormValue(\n value: EastingNorthingState | undefined\n ): string {\n if (!value) {\n return ''\n }\n\n // CYA page format: <<northingvalue, eastingvalue>>\n return `${value.northing}, ${value.easting}`\n }\n\n getDisplayStringFromState(state: FormSubmissionState) {\n const value = this.getFormValueFromState(state)\n\n return this.getDisplayStringFromFormValue(value)\n }\n\n getContextValueFromFormValue(\n value: EastingNorthingState | undefined\n ): string | null {\n if (!value) {\n return null\n }\n\n // Output format: Northing: <<entry>>\\nEasting: <<entry>>\n return `Northing: ${value.northing}\\nEasting: ${value.easting}`\n }\n\n getContextValueFromState(state: FormSubmissionState) {\n const value = this.getFormValueFromState(state)\n\n return this.getContextValueFromFormValue(value)\n }\n\n getViewModel(payload: FormPayload, errors?: FormSubmissionError[]) {\n const viewModel = super.getViewModel(payload, errors)\n return getLocationFieldViewModel(this, viewModel, payload, errors)\n }\n\n isState(value?: FormStateValue | FormState) {\n return EastingNorthingField.isEastingNorthing(value)\n }\n\n /**\n * For error preview page that shows all possible errors on a component\n */\n getAllPossibleErrors(): ErrorMessageTemplateList {\n return EastingNorthingField.getAllPossibleErrors()\n }\n\n /**\n * Static version of getAllPossibleErrors that doesn't require a component instance.\n */\n static getAllPossibleErrors(): ErrorMessageTemplateList {\n return {\n baseErrors: [\n { type: 'required', template: messageTemplate.required },\n {\n type: 'eastingFormat',\n template:\n 'Easting for [short description] must be between 1 and 6 digits'\n },\n {\n type: 'northingFormat',\n template:\n 'Northing for [short description] must be between 1 and 7 digits'\n }\n ],\n advancedSettingsErrors: [\n {\n type: 'eastingMin',\n template: `Easting for [short description] must be between 0 and 700000`\n },\n {\n type: 'eastingMax',\n template: `Easting for [short description] must be between 0 and 700000`\n },\n {\n type: 'northingMin',\n template: `Northing for [short description] must be between ${DEFAULT_NORTHING_MIN} and ${DEFAULT_NORTHING_MAX}`\n },\n {\n type: 'northingMax',\n template: `Northing for [short description] must be between ${DEFAULT_NORTHING_MIN} and ${DEFAULT_NORTHING_MAX}`\n }\n ]\n }\n }\n\n static isEastingNorthing(\n value?: FormStateValue | FormState\n ): value is EastingNorthingState {\n return (\n isFormState(value) &&\n NumberField.isNumber(value.easting) &&\n NumberField.isNumber(value.northing)\n )\n }\n}\n\nexport function getValidatorEastingNorthing(component: EastingNorthingField) {\n return createLocationFieldValidator(component)\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -9,19 +9,7 @@ import { convertToLanguageMessages } from "../../../utils/type-utils.js";
|
|
|
9
9
|
// Precision constants
|
|
10
10
|
// UK latitude/longitude requires high precision for accurate location (within ~11mm)
|
|
11
11
|
const DECIMAL_PRECISION = 7; // 7 decimal places
|
|
12
|
-
const MIN_DECIMAL_PLACES = 1; // At least 1 decimal place required
|
|
13
12
|
|
|
14
|
-
// Latitude length constraints
|
|
15
|
-
// Min: 3 chars for values like "52.1" (2 digits + decimal + 1 decimal place)
|
|
16
|
-
// Max: 10 chars for values like "59.1234567" (2 digits + decimal + 7 decimal places)
|
|
17
|
-
const LATITUDE_MIN_LENGTH = 3;
|
|
18
|
-
const LATITUDE_MAX_LENGTH = 10;
|
|
19
|
-
|
|
20
|
-
// Longitude length constraints
|
|
21
|
-
// Min: 2 chars for values like "-1" or single digit with decimal (needs min decimal places)
|
|
22
|
-
// Max: 10 chars for values like "-1.1234567" (minus + 1 digit + decimal + 7 decimal places)
|
|
23
|
-
const LONGITUDE_MIN_LENGTH = 2;
|
|
24
|
-
const LONGITUDE_MAX_LENGTH = 10;
|
|
25
13
|
export class LatLongField extends FormComponent {
|
|
26
14
|
constructor(def, props) {
|
|
27
15
|
super(def, props);
|
|
@@ -33,32 +21,27 @@ export class LatLongField extends FormComponent {
|
|
|
33
21
|
const isRequired = options.required !== false;
|
|
34
22
|
|
|
35
23
|
// Read schema values from def.schema with fallback defaults
|
|
36
|
-
const latitudeMin = schema?.latitude?.min ?? 49;
|
|
37
|
-
const latitudeMax = schema?.latitude?.max ?? 60;
|
|
38
|
-
const longitudeMin = schema?.longitude?.min ?? -
|
|
39
|
-
const longitudeMax = schema?.longitude?.max ??
|
|
24
|
+
const latitudeMin = schema?.latitude?.min ?? 49.85;
|
|
25
|
+
const latitudeMax = schema?.latitude?.max ?? 60.859;
|
|
26
|
+
const longitudeMin = schema?.longitude?.min ?? -13.687;
|
|
27
|
+
const longitudeMax = schema?.longitude?.max ?? 1.767;
|
|
40
28
|
const customValidationMessages = convertToLanguageMessages({
|
|
41
29
|
'any.required': messageTemplate.objectMissing,
|
|
42
30
|
'number.base': messageTemplate.objectMissing,
|
|
43
31
|
'number.precision': '{{#label}} must have no more than 7 decimal places',
|
|
44
|
-
'number.minPrecision': '{{#label}} must have at least {{#minPrecision}} decimal place',
|
|
45
32
|
'number.unsafe': '{{#label}} must be a valid number'
|
|
46
33
|
});
|
|
47
34
|
const latitudeMessages = convertToLanguageMessages({
|
|
48
35
|
...customValidationMessages,
|
|
49
36
|
'number.base': `Enter a valid latitude for ${this.title} like 51.519450`,
|
|
50
37
|
'number.min': `Latitude for ${this.title} must be between ${latitudeMin} and ${latitudeMax}`,
|
|
51
|
-
'number.max': `Latitude for ${this.title} must be between ${latitudeMin} and ${latitudeMax}
|
|
52
|
-
'number.minLength': `Latitude for ${this.title} must be between 3 and 10 characters`,
|
|
53
|
-
'number.maxLength': `Latitude for ${this.title} must be between 3 and 10 characters`
|
|
38
|
+
'number.max': `Latitude for ${this.title} must be between ${latitudeMin} and ${latitudeMax}`
|
|
54
39
|
});
|
|
55
40
|
const longitudeMessages = convertToLanguageMessages({
|
|
56
41
|
...customValidationMessages,
|
|
57
42
|
'number.base': `Enter a valid longitude for ${this.title} like -0.127758`,
|
|
58
43
|
'number.min': `Longitude for ${this.title} must be between ${longitudeMin} and ${longitudeMax}`,
|
|
59
|
-
'number.max': `Longitude for ${this.title} must be between ${longitudeMin} and ${longitudeMax}
|
|
60
|
-
'number.minLength': `Longitude for ${this.title} must be between 2 and 10 characters`,
|
|
61
|
-
'number.maxLength': `Longitude for ${this.title} must be between 2 and 10 characters`
|
|
44
|
+
'number.max': `Longitude for ${this.title} must be between ${longitudeMin} and ${longitudeMax}`
|
|
62
45
|
});
|
|
63
46
|
this.collection = new ComponentCollection([{
|
|
64
47
|
type: ComponentType.NumberField,
|
|
@@ -67,10 +50,7 @@ export class LatLongField extends FormComponent {
|
|
|
67
50
|
schema: {
|
|
68
51
|
min: latitudeMin,
|
|
69
52
|
max: latitudeMax,
|
|
70
|
-
precision: DECIMAL_PRECISION
|
|
71
|
-
minPrecision: MIN_DECIMAL_PLACES,
|
|
72
|
-
minLength: LATITUDE_MIN_LENGTH,
|
|
73
|
-
maxLength: LATITUDE_MAX_LENGTH
|
|
53
|
+
precision: DECIMAL_PRECISION
|
|
74
54
|
},
|
|
75
55
|
options: {
|
|
76
56
|
required: isRequired,
|
|
@@ -86,10 +66,7 @@ export class LatLongField extends FormComponent {
|
|
|
86
66
|
schema: {
|
|
87
67
|
min: longitudeMin,
|
|
88
68
|
max: longitudeMax,
|
|
89
|
-
precision: DECIMAL_PRECISION
|
|
90
|
-
minPrecision: MIN_DECIMAL_PLACES,
|
|
91
|
-
minLength: LONGITUDE_MIN_LENGTH,
|
|
92
|
-
maxLength: LONGITUDE_MAX_LENGTH
|
|
69
|
+
precision: DECIMAL_PRECISION
|
|
93
70
|
},
|
|
94
71
|
options: {
|
|
95
72
|
required: isRequired,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LatLongField.js","names":["ComponentType","ComponentCollection","FormComponent","isFormState","createLocationFieldValidator","getLocationFieldViewModel","NumberField","messageTemplate","convertToLanguageMessages","DECIMAL_PRECISION","MIN_DECIMAL_PLACES","LATITUDE_MIN_LENGTH","LATITUDE_MAX_LENGTH","LONGITUDE_MIN_LENGTH","LONGITUDE_MAX_LENGTH","LatLongField","constructor","def","props","name","options","schema","isRequired","required","latitudeMin","latitude","min","latitudeMax","max","longitudeMin","longitude","longitudeMax","customValidationMessages","objectMissing","latitudeMessages","title","longitudeMessages","collection","type","precision","minPrecision","minLength","maxLength","optionalText","classes","suffix","parent","custom","getValidatorLatLong","peers","formSchema","stateSchema","getFormValueFromState","state","value","isLatLong","undefined","getDisplayStringFromFormValue","getDisplayStringFromState","getContextValueFromFormValue","getContextValueFromState","getViewModel","payload","errors","viewModel","isState","getAllPossibleErrors","baseErrors","template","advancedSettingsErrors","isNumber","component"],"sources":["../../../../../src/server/plugins/engine/components/LatLongField.ts"],"sourcesContent":["import { ComponentType, type LatLongFieldComponent } from '@defra/forms-model'\nimport { type LanguageMessages, type ObjectSchema } from 'joi'\n\nimport { ComponentCollection } from '~/src/server/plugins/engine/components/ComponentCollection.js'\nimport {\n FormComponent,\n isFormState\n} from '~/src/server/plugins/engine/components/FormComponent.js'\nimport {\n createLocationFieldValidator,\n getLocationFieldViewModel\n} from '~/src/server/plugins/engine/components/LocationFieldHelpers.js'\nimport { NumberField } from '~/src/server/plugins/engine/components/NumberField.js'\nimport { type LatLongState } from '~/src/server/plugins/engine/components/types.js'\nimport { messageTemplate } from '~/src/server/plugins/engine/pageControllers/validationOptions.js'\nimport {\n type ErrorMessageTemplateList,\n type FormPayload,\n type FormState,\n type FormStateValue,\n type FormSubmissionError,\n type FormSubmissionState\n} from '~/src/server/plugins/engine/types.js'\nimport { convertToLanguageMessages } from '~/src/server/utils/type-utils.js'\n\n// Precision constants\n// UK latitude/longitude requires high precision for accurate location (within ~11mm)\nconst DECIMAL_PRECISION = 7 // 7 decimal places\nconst MIN_DECIMAL_PLACES = 1 // At least 1 decimal place required\n\n// Latitude length constraints\n// Min: 3 chars for values like \"52.1\" (2 digits + decimal + 1 decimal place)\n// Max: 10 chars for values like \"59.1234567\" (2 digits + decimal + 7 decimal places)\nconst LATITUDE_MIN_LENGTH = 3\nconst LATITUDE_MAX_LENGTH = 10\n\n// Longitude length constraints\n// Min: 2 chars for values like \"-1\" or single digit with decimal (needs min decimal places)\n// Max: 10 chars for values like \"-1.1234567\" (minus + 1 digit + decimal + 7 decimal places)\nconst LONGITUDE_MIN_LENGTH = 2\nconst LONGITUDE_MAX_LENGTH = 10\n\nexport class LatLongField extends FormComponent {\n declare options: LatLongFieldComponent['options']\n declare formSchema: ObjectSchema<FormPayload>\n declare stateSchema: ObjectSchema<FormState>\n declare collection: ComponentCollection\n\n constructor(\n def: LatLongFieldComponent,\n props: ConstructorParameters<typeof FormComponent>[1]\n ) {\n super(def, props)\n\n const { name, options, schema } = def\n\n const isRequired = options.required !== false\n\n // Read schema values from def.schema with fallback defaults\n const latitudeMin = schema?.latitude?.min ?? 49\n const latitudeMax = schema?.latitude?.max ?? 60\n const longitudeMin = schema?.longitude?.min ?? -9\n const longitudeMax = schema?.longitude?.max ?? 2\n\n const customValidationMessages: LanguageMessages =\n convertToLanguageMessages({\n 'any.required': messageTemplate.objectMissing,\n 'number.base': messageTemplate.objectMissing,\n 'number.precision':\n '{{#label}} must have no more than 7 decimal places',\n 'number.minPrecision':\n '{{#label}} must have at least {{#minPrecision}} decimal place',\n 'number.unsafe': '{{#label}} must be a valid number'\n })\n\n const latitudeMessages: LanguageMessages = convertToLanguageMessages({\n ...customValidationMessages,\n 'number.base': `Enter a valid latitude for ${this.title} like 51.519450`,\n 'number.min': `Latitude for ${this.title} must be between ${latitudeMin} and ${latitudeMax}`,\n 'number.max': `Latitude for ${this.title} must be between ${latitudeMin} and ${latitudeMax}`,\n 'number.minLength': `Latitude for ${this.title} must be between 3 and 10 characters`,\n 'number.maxLength': `Latitude for ${this.title} must be between 3 and 10 characters`\n })\n\n const longitudeMessages: LanguageMessages = convertToLanguageMessages({\n ...customValidationMessages,\n 'number.base': `Enter a valid longitude for ${this.title} like -0.127758`,\n 'number.min': `Longitude for ${this.title} must be between ${longitudeMin} and ${longitudeMax}`,\n 'number.max': `Longitude for ${this.title} must be between ${longitudeMin} and ${longitudeMax}`,\n 'number.minLength': `Longitude for ${this.title} must be between 2 and 10 characters`,\n 'number.maxLength': `Longitude for ${this.title} must be between 2 and 10 characters`\n })\n\n this.collection = new ComponentCollection(\n [\n {\n type: ComponentType.NumberField,\n name: `${name}__latitude`,\n title: 'Latitude',\n schema: {\n min: latitudeMin,\n max: latitudeMax,\n precision: DECIMAL_PRECISION,\n minPrecision: MIN_DECIMAL_PLACES,\n minLength: LATITUDE_MIN_LENGTH,\n maxLength: LATITUDE_MAX_LENGTH\n },\n options: {\n required: isRequired,\n optionalText: true,\n classes: 'govuk-input--width-10',\n suffix: '°',\n customValidationMessages: latitudeMessages\n }\n },\n {\n type: ComponentType.NumberField,\n name: `${name}__longitude`,\n title: 'Longitude',\n schema: {\n min: longitudeMin,\n max: longitudeMax,\n precision: DECIMAL_PRECISION,\n minPrecision: MIN_DECIMAL_PLACES,\n minLength: LONGITUDE_MIN_LENGTH,\n maxLength: LONGITUDE_MAX_LENGTH\n },\n options: {\n required: isRequired,\n optionalText: true,\n classes: 'govuk-input--width-10',\n suffix: '°',\n customValidationMessages: longitudeMessages\n }\n }\n ],\n { ...props, parent: this },\n {\n custom: getValidatorLatLong(this),\n peers: [`${name}__latitude`, `${name}__longitude`]\n }\n )\n\n this.options = options\n this.formSchema = this.collection.formSchema\n this.stateSchema = this.collection.stateSchema\n }\n\n getFormValueFromState(state: FormSubmissionState) {\n const value = super.getFormValueFromState(state)\n return LatLongField.isLatLong(value) ? value : undefined\n }\n\n getDisplayStringFromFormValue(value: LatLongState | undefined): string {\n if (!value) {\n return ''\n }\n\n // CYA page format: <<latvalue, langvalue>>\n return `${value.latitude}, ${value.longitude}`\n }\n\n getDisplayStringFromState(state: FormSubmissionState) {\n const value = this.getFormValueFromState(state)\n\n return this.getDisplayStringFromFormValue(value)\n }\n\n getContextValueFromFormValue(value: LatLongState | undefined): string | null {\n if (!value) {\n return null\n }\n\n // Output format: Lat: <<entry>>\\nLong: <<entry>>\n return `Lat: ${value.latitude}\\nLong: ${value.longitude}`\n }\n\n getContextValueFromState(state: FormSubmissionState) {\n const value = this.getFormValueFromState(state)\n\n return this.getContextValueFromFormValue(value)\n }\n\n getViewModel(payload: FormPayload, errors?: FormSubmissionError[]) {\n const viewModel = super.getViewModel(payload, errors)\n return getLocationFieldViewModel(this, viewModel, payload, errors)\n }\n\n isState(value?: FormStateValue | FormState) {\n return LatLongField.isLatLong(value)\n }\n\n /**\n * For error preview page that shows all possible errors on a component\n */\n getAllPossibleErrors(): ErrorMessageTemplateList {\n return LatLongField.getAllPossibleErrors()\n }\n\n /**\n * Static version of getAllPossibleErrors that doesn't require a component instance.\n */\n static getAllPossibleErrors(): ErrorMessageTemplateList {\n return {\n baseErrors: [\n { type: 'required', template: messageTemplate.required },\n {\n type: 'latitudeFormat',\n template:\n 'Enter a valid latitude for [short description] like 51.519450'\n },\n {\n type: 'longitudeFormat',\n template:\n 'Enter a valid longitude for [short description] like -0.127758'\n }\n ],\n advancedSettingsErrors: [\n {\n type: 'latitudeMin',\n template: 'Latitude for [short description] must be between 49 and 60'\n },\n {\n type: 'latitudeMax',\n template: 'Latitude for [short description] must be between 49 and 60'\n },\n {\n type: 'longitudeMin',\n template: 'Longitude for [short description] must be between -9 and 2'\n },\n {\n type: 'longitudeMax',\n template: 'Longitude for [short description] must be between -9 and 2'\n }\n ]\n }\n }\n\n static isLatLong(value?: FormStateValue | FormState): value is LatLongState {\n return (\n isFormState(value) &&\n NumberField.isNumber(value.latitude) &&\n NumberField.isNumber(value.longitude)\n )\n }\n}\n\nexport function getValidatorLatLong(component: LatLongField) {\n return createLocationFieldValidator(component)\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"LatLongField.js","names":["ComponentType","ComponentCollection","FormComponent","isFormState","createLocationFieldValidator","getLocationFieldViewModel","NumberField","messageTemplate","convertToLanguageMessages","DECIMAL_PRECISION","LatLongField","constructor","def","props","name","options","schema","isRequired","required","latitudeMin","latitude","min","latitudeMax","max","longitudeMin","longitude","longitudeMax","customValidationMessages","objectMissing","latitudeMessages","title","longitudeMessages","collection","type","precision","optionalText","classes","suffix","parent","custom","getValidatorLatLong","peers","formSchema","stateSchema","getFormValueFromState","state","value","isLatLong","undefined","getDisplayStringFromFormValue","getDisplayStringFromState","getContextValueFromFormValue","getContextValueFromState","getViewModel","payload","errors","viewModel","isState","getAllPossibleErrors","baseErrors","template","advancedSettingsErrors","isNumber","component"],"sources":["../../../../../src/server/plugins/engine/components/LatLongField.ts"],"sourcesContent":["import { ComponentType, type LatLongFieldComponent } from '@defra/forms-model'\nimport { type LanguageMessages, type ObjectSchema } from 'joi'\n\nimport { ComponentCollection } from '~/src/server/plugins/engine/components/ComponentCollection.js'\nimport {\n FormComponent,\n isFormState\n} from '~/src/server/plugins/engine/components/FormComponent.js'\nimport {\n createLocationFieldValidator,\n getLocationFieldViewModel\n} from '~/src/server/plugins/engine/components/LocationFieldHelpers.js'\nimport { NumberField } from '~/src/server/plugins/engine/components/NumberField.js'\nimport { type LatLongState } from '~/src/server/plugins/engine/components/types.js'\nimport { messageTemplate } from '~/src/server/plugins/engine/pageControllers/validationOptions.js'\nimport {\n type ErrorMessageTemplateList,\n type FormPayload,\n type FormState,\n type FormStateValue,\n type FormSubmissionError,\n type FormSubmissionState\n} from '~/src/server/plugins/engine/types.js'\nimport { convertToLanguageMessages } from '~/src/server/utils/type-utils.js'\n\n// Precision constants\n// UK latitude/longitude requires high precision for accurate location (within ~11mm)\nconst DECIMAL_PRECISION = 7 // 7 decimal places\n\nexport class LatLongField extends FormComponent {\n declare options: LatLongFieldComponent['options']\n declare formSchema: ObjectSchema<FormPayload>\n declare stateSchema: ObjectSchema<FormState>\n declare collection: ComponentCollection\n\n constructor(\n def: LatLongFieldComponent,\n props: ConstructorParameters<typeof FormComponent>[1]\n ) {\n super(def, props)\n\n const { name, options, schema } = def\n\n const isRequired = options.required !== false\n\n // Read schema values from def.schema with fallback defaults\n const latitudeMin = schema?.latitude?.min ?? 49.85\n const latitudeMax = schema?.latitude?.max ?? 60.859\n const longitudeMin = schema?.longitude?.min ?? -13.687\n const longitudeMax = schema?.longitude?.max ?? 1.767\n\n const customValidationMessages: LanguageMessages =\n convertToLanguageMessages({\n 'any.required': messageTemplate.objectMissing,\n 'number.base': messageTemplate.objectMissing,\n 'number.precision':\n '{{#label}} must have no more than 7 decimal places',\n 'number.unsafe': '{{#label}} must be a valid number'\n })\n\n const latitudeMessages: LanguageMessages = convertToLanguageMessages({\n ...customValidationMessages,\n 'number.base': `Enter a valid latitude for ${this.title} like 51.519450`,\n 'number.min': `Latitude for ${this.title} must be between ${latitudeMin} and ${latitudeMax}`,\n 'number.max': `Latitude for ${this.title} must be between ${latitudeMin} and ${latitudeMax}`\n })\n\n const longitudeMessages: LanguageMessages = convertToLanguageMessages({\n ...customValidationMessages,\n 'number.base': `Enter a valid longitude for ${this.title} like -0.127758`,\n 'number.min': `Longitude for ${this.title} must be between ${longitudeMin} and ${longitudeMax}`,\n 'number.max': `Longitude for ${this.title} must be between ${longitudeMin} and ${longitudeMax}`\n })\n\n this.collection = new ComponentCollection(\n [\n {\n type: ComponentType.NumberField,\n name: `${name}__latitude`,\n title: 'Latitude',\n schema: {\n min: latitudeMin,\n max: latitudeMax,\n precision: DECIMAL_PRECISION\n },\n options: {\n required: isRequired,\n optionalText: true,\n classes: 'govuk-input--width-10',\n suffix: '°',\n customValidationMessages: latitudeMessages\n }\n },\n {\n type: ComponentType.NumberField,\n name: `${name}__longitude`,\n title: 'Longitude',\n schema: {\n min: longitudeMin,\n max: longitudeMax,\n precision: DECIMAL_PRECISION\n },\n options: {\n required: isRequired,\n optionalText: true,\n classes: 'govuk-input--width-10',\n suffix: '°',\n customValidationMessages: longitudeMessages\n }\n }\n ],\n { ...props, parent: this },\n {\n custom: getValidatorLatLong(this),\n peers: [`${name}__latitude`, `${name}__longitude`]\n }\n )\n\n this.options = options\n this.formSchema = this.collection.formSchema\n this.stateSchema = this.collection.stateSchema\n }\n\n getFormValueFromState(state: FormSubmissionState) {\n const value = super.getFormValueFromState(state)\n return LatLongField.isLatLong(value) ? value : undefined\n }\n\n getDisplayStringFromFormValue(value: LatLongState | undefined): string {\n if (!value) {\n return ''\n }\n\n // CYA page format: <<latvalue, langvalue>>\n return `${value.latitude}, ${value.longitude}`\n }\n\n getDisplayStringFromState(state: FormSubmissionState) {\n const value = this.getFormValueFromState(state)\n\n return this.getDisplayStringFromFormValue(value)\n }\n\n getContextValueFromFormValue(value: LatLongState | undefined): string | null {\n if (!value) {\n return null\n }\n\n // Output format: Lat: <<entry>>\\nLong: <<entry>>\n return `Lat: ${value.latitude}\\nLong: ${value.longitude}`\n }\n\n getContextValueFromState(state: FormSubmissionState) {\n const value = this.getFormValueFromState(state)\n\n return this.getContextValueFromFormValue(value)\n }\n\n getViewModel(payload: FormPayload, errors?: FormSubmissionError[]) {\n const viewModel = super.getViewModel(payload, errors)\n return getLocationFieldViewModel(this, viewModel, payload, errors)\n }\n\n isState(value?: FormStateValue | FormState) {\n return LatLongField.isLatLong(value)\n }\n\n /**\n * For error preview page that shows all possible errors on a component\n */\n getAllPossibleErrors(): ErrorMessageTemplateList {\n return LatLongField.getAllPossibleErrors()\n }\n\n /**\n * Static version of getAllPossibleErrors that doesn't require a component instance.\n */\n static getAllPossibleErrors(): ErrorMessageTemplateList {\n return {\n baseErrors: [\n { type: 'required', template: messageTemplate.required },\n {\n type: 'latitudeFormat',\n template:\n 'Enter a valid latitude for [short description] like 51.519450'\n },\n {\n type: 'longitudeFormat',\n template:\n 'Enter a valid longitude for [short description] like -0.127758'\n }\n ],\n advancedSettingsErrors: [\n {\n type: 'latitudeMin',\n template: 'Latitude for [short description] must be between 49 and 60'\n },\n {\n type: 'latitudeMax',\n template: 'Latitude for [short description] must be between 49 and 60'\n },\n {\n type: 'longitudeMin',\n template: 'Longitude for [short description] must be between -9 and 2'\n },\n {\n type: 'longitudeMax',\n template: 'Longitude for [short description] must be between -9 and 2'\n }\n ]\n }\n }\n\n static isLatLong(value?: FormStateValue | FormState): value is LatLongState {\n return (\n isFormState(value) &&\n NumberField.isNumber(value.latitude) &&\n NumberField.isNumber(value.longitude)\n )\n }\n}\n\nexport function getValidatorLatLong(component: LatLongField) {\n return createLocationFieldValidator(component)\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type FormComponentsDef } from '@defra/forms-model';
|
|
2
|
-
import
|
|
2
|
+
import { type LanguageMessages, type StringSchema } from 'joi';
|
|
3
3
|
import { FormComponent } from '~/src/server/plugins/engine/components/FormComponent.js';
|
|
4
4
|
import { type ErrorMessageTemplateList, type FormPayload, type FormState, type FormStateValue, type FormSubmissionError, type FormSubmissionState } from '~/src/server/plugins/engine/types.js';
|
|
5
5
|
interface LocationFieldOptions {
|
|
@@ -12,8 +12,6 @@ interface LocationFieldOptions {
|
|
|
12
12
|
interface ValidationConfig {
|
|
13
13
|
pattern: RegExp;
|
|
14
14
|
patternErrorMessage: string;
|
|
15
|
-
customValidation?: (value: string, helpers: joi.CustomHelpers) => string | joi.ErrorReport;
|
|
16
|
-
additionalMessages?: LanguageMessages;
|
|
17
15
|
}
|
|
18
16
|
/**
|
|
19
17
|
* Abstract base class for location-based field components
|
|
@@ -18,21 +18,14 @@ export class LocationFieldBase extends FormComponent {
|
|
|
18
18
|
addClassOptionIfNone(locationOptions, 'govuk-input--width-10');
|
|
19
19
|
const config = this.getValidationConfig();
|
|
20
20
|
let formSchema = joi.string().trim().label(this.label).required().pattern(config.pattern).messages({
|
|
21
|
-
'string.pattern.base': config.patternErrorMessage
|
|
22
|
-
...config.additionalMessages
|
|
21
|
+
'string.pattern.base': config.patternErrorMessage
|
|
23
22
|
});
|
|
24
|
-
if (config.customValidation) {
|
|
25
|
-
formSchema = formSchema.custom(config.customValidation);
|
|
26
|
-
}
|
|
27
23
|
if (locationOptions.required === false) {
|
|
28
24
|
formSchema = formSchema.allow('');
|
|
29
25
|
}
|
|
30
26
|
if (locationOptions.customValidationMessage) {
|
|
31
27
|
const message = locationOptions.customValidationMessage;
|
|
32
28
|
const messageKeys = ['any.required', 'string.empty', 'string.pattern.base'];
|
|
33
|
-
if (config.additionalMessages) {
|
|
34
|
-
messageKeys.push(...Object.keys(config.additionalMessages));
|
|
35
|
-
}
|
|
36
29
|
const messages = messageKeys.reduce((acc, key) => {
|
|
37
30
|
acc[key] = message;
|
|
38
31
|
return acc;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LocationFieldBase.js","names":["joi","FormComponent","isFormValue","addClassOptionIfNone","markdown","messageTemplate","LocationFieldBase","instructionText","constructor","def","props","options","locationOptions","config","getValidationConfig","formSchema","string","trim","label","required","pattern","messages","patternErrorMessage","
|
|
1
|
+
{"version":3,"file":"LocationFieldBase.js","names":["joi","FormComponent","isFormValue","addClassOptionIfNone","markdown","messageTemplate","LocationFieldBase","instructionText","constructor","def","props","options","locationOptions","config","getValidationConfig","formSchema","string","trim","label","required","pattern","messages","patternErrorMessage","allow","customValidationMessage","message","messageKeys","reduce","acc","key","customValidationMessages","default","stateSchema","getFormValueFromState","state","name","getFormValue","value","isValue","undefined","isText","getViewModel","payload","errors","viewModel","parse","async","getAllPossibleErrors","baseErrors","type","template","getErrorTemplates","advancedSettingsErrors"],"sources":["../../../../../src/server/plugins/engine/components/LocationFieldBase.ts"],"sourcesContent":["import { type FormComponentsDef } from '@defra/forms-model'\nimport joi, { type LanguageMessages, type StringSchema } from 'joi'\n\nimport {\n FormComponent,\n isFormValue\n} from '~/src/server/plugins/engine/components/FormComponent.js'\nimport { addClassOptionIfNone } from '~/src/server/plugins/engine/components/helpers/index.js'\nimport { markdown } from '~/src/server/plugins/engine/components/markdownParser.js'\nimport { messageTemplate } from '~/src/server/plugins/engine/pageControllers/validationOptions.js'\nimport {\n type ErrorMessageTemplateList,\n type FormPayload,\n type FormState,\n type FormStateValue,\n type FormSubmissionError,\n type FormSubmissionState\n} from '~/src/server/plugins/engine/types.js'\n\ninterface LocationFieldOptions {\n instructionText?: string\n required?: boolean\n customValidationMessage?: string\n customValidationMessages?: LanguageMessages\n classes?: string\n}\n\ninterface ValidationConfig {\n pattern: RegExp\n patternErrorMessage: string\n}\n\n/**\n * Abstract base class for location-based field components\n */\nexport abstract class LocationFieldBase extends FormComponent {\n declare options: LocationFieldOptions\n declare formSchema: StringSchema\n declare stateSchema: StringSchema\n instructionText?: string\n\n protected abstract getValidationConfig(): ValidationConfig\n protected abstract getErrorTemplates(): {\n type: string\n template: string\n }[]\n\n constructor(\n def: FormComponentsDef,\n props: ConstructorParameters<typeof FormComponent>[1]\n ) {\n super(def, props)\n\n const { options } = def\n const locationOptions = options as LocationFieldOptions\n this.instructionText = locationOptions.instructionText\n\n addClassOptionIfNone(locationOptions, 'govuk-input--width-10')\n\n const config = this.getValidationConfig()\n\n let formSchema = joi\n .string()\n .trim()\n .label(this.label)\n .required()\n .pattern(config.pattern)\n .messages({\n 'string.pattern.base': config.patternErrorMessage\n })\n\n if (locationOptions.required === false) {\n formSchema = formSchema.allow('')\n }\n\n if (locationOptions.customValidationMessage) {\n const message = locationOptions.customValidationMessage\n const messageKeys = [\n 'any.required',\n 'string.empty',\n 'string.pattern.base'\n ]\n\n const messages = messageKeys.reduce<LanguageMessages>((acc, key) => {\n acc[key] = message\n return acc\n }, {})\n\n formSchema = formSchema.messages(messages)\n } else if (locationOptions.customValidationMessages) {\n formSchema = formSchema.messages(locationOptions.customValidationMessages)\n }\n\n this.formSchema = formSchema.default('')\n this.stateSchema = formSchema.default(null).allow(null)\n this.options = locationOptions\n }\n\n getFormValueFromState(state: FormSubmissionState) {\n const { name } = this\n return this.getFormValue(state[name])\n }\n\n getFormValue(value?: FormStateValue | FormState) {\n return this.isValue(value) ? value : undefined\n }\n\n isValue(value?: FormStateValue | FormState): value is string {\n return LocationFieldBase.isText(value)\n }\n\n getViewModel(payload: FormPayload, errors?: FormSubmissionError[]) {\n const viewModel = super.getViewModel(payload, errors)\n\n if (this.instructionText) {\n return {\n ...viewModel,\n instructionText: markdown.parse(this.instructionText, { async: false })\n }\n }\n\n return viewModel\n }\n\n getAllPossibleErrors(): ErrorMessageTemplateList {\n return {\n baseErrors: [\n { type: 'required', template: messageTemplate.required },\n ...this.getErrorTemplates()\n ],\n advancedSettingsErrors: []\n }\n }\n\n static isText(value?: FormStateValue | FormState): value is string {\n return isFormValue(value) && typeof value === 'string'\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import { type NationalGridFieldNumberFieldComponent } from '@defra/forms-model';
|
|
2
|
-
import type joi from 'joi';
|
|
3
2
|
import { LocationFieldBase } from '~/src/server/plugins/engine/components/LocationFieldBase.js';
|
|
4
3
|
export declare class NationalGridFieldNumberField extends LocationFieldBase {
|
|
5
4
|
options: NationalGridFieldNumberFieldComponent['options'];
|
|
6
5
|
protected getValidationConfig(): {
|
|
7
6
|
pattern: RegExp;
|
|
8
7
|
patternErrorMessage: string;
|
|
9
|
-
customValidation: (value: string, helpers: joi.CustomHelpers) => string | joi.ErrorReport;
|
|
10
8
|
};
|
|
11
9
|
protected getErrorTemplates(): {
|
|
12
10
|
type: string;
|
|
@@ -1,25 +1,14 @@
|
|
|
1
1
|
import { LocationFieldBase } from "./LocationFieldBase.js";
|
|
2
2
|
export class NationalGridFieldNumberField extends LocationFieldBase {
|
|
3
3
|
getValidationConfig() {
|
|
4
|
+
// Regex for OS grid references and parcel IDs
|
|
5
|
+
// Validates specific valid OS grid letter combinations with:
|
|
6
|
+
// - 2 letters & 8 digits in 2 blocks of 4 (parcel ID) e.g., ST 6789 6789
|
|
7
|
+
// - 2 letters & 10 digits in 2 blocks of 5 (OS grid reference) e.g., SO 12345 12345
|
|
8
|
+
const pattern = /^((([sS]|[nN])[a-hA-Hj-zJ-Z])|(([tT]|[oO])[abfglmqrvwABFGLMQRVW])|([hH][l-zL-Z])|([jJ][lmqrvwLMQRVW]))\s?(([0-9]{4})\s?([0-9]{4})|([0-9]{5})\s?([0-9]{5}))$/;
|
|
4
9
|
return {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
patternErrorMessage: `Enter a valid National Grid field number for ${this.title} like NG 1234 5678`,
|
|
8
|
-
customValidation: (value, helpers) => {
|
|
9
|
-
// Strip spaces and commas for validation
|
|
10
|
-
const cleanValue = value.replace(/[\s,]/g, '');
|
|
11
|
-
|
|
12
|
-
// Check if it matches the exact pattern after cleaning
|
|
13
|
-
if (!/^[A-Z]{2}\d{8}$/i.test(cleanValue)) {
|
|
14
|
-
return helpers.error('string.pattern.base');
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// Format with spaces per GDS guidance: NG 1234 5678
|
|
18
|
-
const letters = cleanValue.substring(0, 2);
|
|
19
|
-
const numbers = cleanValue.substring(2);
|
|
20
|
-
const formattedValue = `${letters} ${numbers.substring(0, 4)} ${numbers.substring(4)}`;
|
|
21
|
-
return formattedValue;
|
|
22
|
-
}
|
|
10
|
+
pattern,
|
|
11
|
+
patternErrorMessage: `Enter a valid National Grid field number for ${this.title} like NG 1234 5678`
|
|
23
12
|
};
|
|
24
13
|
}
|
|
25
14
|
getErrorTemplates() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NationalGridFieldNumberField.js","names":["LocationFieldBase","NationalGridFieldNumberField","getValidationConfig","pattern","patternErrorMessage","title","
|
|
1
|
+
{"version":3,"file":"NationalGridFieldNumberField.js","names":["LocationFieldBase","NationalGridFieldNumberField","getValidationConfig","pattern","patternErrorMessage","title","getErrorTemplates","type","template","getAllPossibleErrors","instance","Object","create","prototype"],"sources":["../../../../../src/server/plugins/engine/components/NationalGridFieldNumberField.ts"],"sourcesContent":["import { type NationalGridFieldNumberFieldComponent } from '@defra/forms-model'\n\nimport { LocationFieldBase } from '~/src/server/plugins/engine/components/LocationFieldBase.js'\n\nexport class NationalGridFieldNumberField extends LocationFieldBase {\n declare options: NationalGridFieldNumberFieldComponent['options']\n\n protected getValidationConfig() {\n // Regex for OS grid references and parcel IDs\n // Validates specific valid OS grid letter combinations with:\n // - 2 letters & 8 digits in 2 blocks of 4 (parcel ID) e.g., ST 6789 6789\n // - 2 letters & 10 digits in 2 blocks of 5 (OS grid reference) e.g., SO 12345 12345\n const pattern =\n /^((([sS]|[nN])[a-hA-Hj-zJ-Z])|(([tT]|[oO])[abfglmqrvwABFGLMQRVW])|([hH][l-zL-Z])|([jJ][lmqrvwLMQRVW]))\\s?(([0-9]{4})\\s?([0-9]{4})|([0-9]{5})\\s?([0-9]{5}))$/\n\n return {\n pattern,\n patternErrorMessage: `Enter a valid National Grid field number for ${this.title} like NG 1234 5678`\n }\n }\n\n protected getErrorTemplates() {\n return [\n {\n type: 'pattern',\n template:\n 'Enter a valid National Grid field number for [short description] like NG 1234 5678'\n }\n ]\n }\n\n /**\n * Static version of getAllPossibleErrors that doesn't require a component instance.\n */\n static getAllPossibleErrors() {\n const instance = Object.create(\n NationalGridFieldNumberField.prototype\n ) as NationalGridFieldNumberField\n return instance.getAllPossibleErrors()\n }\n}\n"],"mappings":"AAEA,SAASA,iBAAiB;AAE1B,OAAO,MAAMC,4BAA4B,SAASD,iBAAiB,CAAC;EAGxDE,mBAAmBA,CAAA,EAAG;IAC9B;IACA;IACA;IACA;IACA,MAAMC,OAAO,GACX,6JAA6J;IAE/J,OAAO;MACLA,OAAO;MACPC,mBAAmB,EAAE,gDAAgD,IAAI,CAACC,KAAK;IACjF,CAAC;EACH;EAEUC,iBAAiBA,CAAA,EAAG;IAC5B,OAAO,CACL;MACEC,IAAI,EAAE,SAAS;MACfC,QAAQ,EACN;IACJ,CAAC,CACF;EACH;;EAEA;AACF;AACA;EACE,OAAOC,oBAAoBA,CAAA,EAAG;IAC5B,MAAMC,QAAQ,GAAGC,MAAM,CAACC,MAAM,CAC5BX,4BAA4B,CAACY,SAC/B,CAAiC;IACjC,OAAOH,QAAQ,CAACD,oBAAoB,CAAC,CAAC;EACxC;AACF","ignoreList":[]}
|
|
@@ -69,22 +69,4 @@ export declare class NumberField extends FormComponent {
|
|
|
69
69
|
static getAllPossibleErrors(): ErrorMessageTemplateList;
|
|
70
70
|
static isNumber(value?: FormStateValue | FormState): value is number;
|
|
71
71
|
}
|
|
72
|
-
/**
|
|
73
|
-
* Validates string length of a numeric value
|
|
74
|
-
* @param value - The numeric value to validate
|
|
75
|
-
* @param minLength - Minimum required string length
|
|
76
|
-
* @param maxLength - Maximum allowed string length
|
|
77
|
-
* @returns Object with validation result
|
|
78
|
-
*/
|
|
79
|
-
export declare function validateStringLength(value: number, minLength?: number, maxLength?: number): {
|
|
80
|
-
isValid: boolean;
|
|
81
|
-
error?: 'minLength' | 'maxLength';
|
|
82
|
-
};
|
|
83
|
-
/**
|
|
84
|
-
* Validates minimum decimal precision
|
|
85
|
-
* @param value - The numeric value to validate
|
|
86
|
-
* @param minPrecision - Minimum required decimal places
|
|
87
|
-
* @returns true if valid, false if invalid
|
|
88
|
-
*/
|
|
89
|
-
export declare function validateMinimumPrecision(value: number, minPrecision: number): boolean;
|
|
90
72
|
export declare function getValidatorPrecision(component: NumberField): joi.CustomValidator;
|
|
@@ -134,84 +134,6 @@ export class NumberField extends FormComponent {
|
|
|
134
134
|
return typeof value === 'number';
|
|
135
135
|
}
|
|
136
136
|
}
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Validates string length of a numeric value
|
|
140
|
-
* @param value - The numeric value to validate
|
|
141
|
-
* @param minLength - Minimum required string length
|
|
142
|
-
* @param maxLength - Maximum allowed string length
|
|
143
|
-
* @returns Object with validation result
|
|
144
|
-
*/
|
|
145
|
-
export function validateStringLength(value, minLength, maxLength) {
|
|
146
|
-
if (typeof minLength !== 'number' && typeof maxLength !== 'number') {
|
|
147
|
-
return {
|
|
148
|
-
isValid: true
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
const valueStr = String(value);
|
|
152
|
-
if (typeof minLength === 'number' && valueStr.length < minLength) {
|
|
153
|
-
return {
|
|
154
|
-
isValid: false,
|
|
155
|
-
error: 'minLength'
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
if (typeof maxLength === 'number' && valueStr.length > maxLength) {
|
|
159
|
-
return {
|
|
160
|
-
isValid: false,
|
|
161
|
-
error: 'maxLength'
|
|
162
|
-
};
|
|
163
|
-
}
|
|
164
|
-
return {
|
|
165
|
-
isValid: true
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* Validates minimum decimal precision
|
|
171
|
-
* @param value - The numeric value to validate
|
|
172
|
-
* @param minPrecision - Minimum required decimal places
|
|
173
|
-
* @returns true if valid, false if invalid
|
|
174
|
-
*/
|
|
175
|
-
export function validateMinimumPrecision(value, minPrecision) {
|
|
176
|
-
if (Number.isInteger(value)) {
|
|
177
|
-
return false;
|
|
178
|
-
}
|
|
179
|
-
const valueStr = String(value);
|
|
180
|
-
const decimalIndex = valueStr.indexOf('.');
|
|
181
|
-
if (decimalIndex !== -1) {
|
|
182
|
-
const decimalPlaces = valueStr.length - decimalIndex - 1;
|
|
183
|
-
return decimalPlaces >= minPrecision;
|
|
184
|
-
}
|
|
185
|
-
return false;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* Helper function to handle length validation errors
|
|
190
|
-
* Returns the appropriate error response based on the validation result
|
|
191
|
-
*/
|
|
192
|
-
function handleLengthValidationError(lengthCheck, helpers, custom, minLength, maxLength) {
|
|
193
|
-
if (!lengthCheck.isValid && lengthCheck.error) {
|
|
194
|
-
const errorType = `number.${lengthCheck.error}`;
|
|
195
|
-
if (custom) {
|
|
196
|
-
// Only pass the relevant length value in context
|
|
197
|
-
const contextData = lengthCheck.error === 'minLength' ? {
|
|
198
|
-
minLength: minLength ?? 0
|
|
199
|
-
} : {
|
|
200
|
-
maxLength: maxLength ?? 0
|
|
201
|
-
};
|
|
202
|
-
return helpers.message({
|
|
203
|
-
custom
|
|
204
|
-
}, contextData);
|
|
205
|
-
}
|
|
206
|
-
const context = lengthCheck.error === 'minLength' ? {
|
|
207
|
-
minLength: minLength ?? 0
|
|
208
|
-
} : {
|
|
209
|
-
maxLength: maxLength ?? 0
|
|
210
|
-
};
|
|
211
|
-
return helpers.error(errorType, context);
|
|
212
|
-
}
|
|
213
|
-
return null;
|
|
214
|
-
}
|
|
215
137
|
export function getValidatorPrecision(component) {
|
|
216
138
|
const validator = (value, helpers) => {
|
|
217
139
|
const {
|
|
@@ -222,24 +144,16 @@ export function getValidatorPrecision(component) {
|
|
|
222
144
|
customValidationMessage: custom
|
|
223
145
|
} = options;
|
|
224
146
|
const {
|
|
225
|
-
precision: limit
|
|
226
|
-
minPrecision,
|
|
227
|
-
minLength,
|
|
228
|
-
maxLength
|
|
147
|
+
precision: limit
|
|
229
148
|
} = schema;
|
|
230
149
|
if (!limit || limit <= 0) {
|
|
231
|
-
const lengthCheck = validateStringLength(value, minLength, maxLength);
|
|
232
|
-
const error = handleLengthValidationError(lengthCheck, helpers, custom, minLength, maxLength);
|
|
233
|
-
if (error) return error;
|
|
234
150
|
return value;
|
|
235
151
|
}
|
|
236
|
-
|
|
237
|
-
// Validate precision (max decimal places)
|
|
238
152
|
const validationSchema = joi.number().precision(limit).prefs({
|
|
239
153
|
convert: false
|
|
240
154
|
});
|
|
241
155
|
try {
|
|
242
|
-
joi.attempt(value, validationSchema);
|
|
156
|
+
return joi.attempt(value, validationSchema);
|
|
243
157
|
} catch {
|
|
244
158
|
return custom ? helpers.message({
|
|
245
159
|
custom
|
|
@@ -249,21 +163,6 @@ export function getValidatorPrecision(component) {
|
|
|
249
163
|
limit
|
|
250
164
|
});
|
|
251
165
|
}
|
|
252
|
-
|
|
253
|
-
// Validate minimum precision (min decimal places)
|
|
254
|
-
if (typeof minPrecision === 'number' && minPrecision > 0) {
|
|
255
|
-
if (!validateMinimumPrecision(value, minPrecision)) {
|
|
256
|
-
return helpers.error('number.minPrecision', {
|
|
257
|
-
minPrecision
|
|
258
|
-
});
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
// Check string length validation after precision checks
|
|
263
|
-
const lengthCheck = validateStringLength(value, minLength, maxLength);
|
|
264
|
-
const error = handleLengthValidationError(lengthCheck, helpers, custom, minLength, maxLength);
|
|
265
|
-
if (error) return error;
|
|
266
|
-
return value;
|
|
267
166
|
};
|
|
268
167
|
return validator;
|
|
269
168
|
}
|