@defra/forms-engine-plugin 5.0.0-alpha.12 → 5.0.0-alpha.13
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/.public/javascripts/shared.min.js +1 -1
- package/.public/javascripts/shared.min.js.map +1 -1
- package/.server/client/javascripts/geospatial-map.d.ts +153 -5
- package/.server/client/javascripts/geospatial-map.js +147 -46
- package/.server/client/javascripts/geospatial-map.js.map +1 -1
- package/.server/client/javascripts/location-map.d.ts +38 -0
- package/.server/client/javascripts/location-map.js +42 -2
- package/.server/client/javascripts/location-map.js.map +1 -1
- package/.server/client/javascripts/map-constants.d.ts +3 -0
- package/.server/client/javascripts/map-constants.js +4 -0
- package/.server/client/javascripts/map-constants.js.map +1 -0
- package/.server/client/javascripts/map.d.ts +4 -0
- package/.server/client/javascripts/map.js +5 -0
- package/.server/client/javascripts/map.js.map +1 -1
- package/.server/server/plugins/engine/__stubs__/definitions.d.ts +29 -0
- package/.server/server/plugins/engine/__stubs__/definitions.js +125 -0
- package/.server/server/plugins/engine/__stubs__/definitions.js.map +1 -0
- package/.server/server/plugins/engine/components/EastingNorthingField.d.ts +2 -0
- package/.server/server/plugins/engine/components/GeospatialField.d.ts +1 -0
- package/.server/server/plugins/engine/components/GeospatialField.js +1 -0
- package/.server/server/plugins/engine/components/GeospatialField.js.map +1 -1
- package/.server/server/plugins/engine/components/LatLongField.d.ts +2 -0
- package/.server/server/plugins/engine/components/LocationFieldBase.js +3 -0
- package/.server/server/plugins/engine/components/LocationFieldBase.js.map +1 -1
- package/.server/server/plugins/engine/components/LocationFieldHelpers.d.ts +2 -0
- package/.server/server/plugins/engine/components/LocationFieldHelpers.js +2 -1
- package/.server/server/plugins/engine/components/LocationFieldHelpers.js.map +1 -1
- package/.server/server/plugins/engine/components/helpers/components.js +2 -1
- package/.server/server/plugins/engine/components/helpers/components.js.map +1 -1
- package/.server/server/plugins/engine/errors.d.ts +49 -0
- package/.server/server/plugins/engine/errors.js +75 -0
- package/.server/server/plugins/engine/errors.js.map +1 -0
- package/.server/server/plugins/engine/i18n/translations/cy.json +51 -1
- package/.server/server/plugins/engine/i18n/translations/en-GB.json +51 -1
- package/.server/server/plugins/engine/models/FormModel.js +10 -2
- package/.server/server/plugins/engine/models/FormModel.js.map +1 -1
- package/.server/server/plugins/engine/pageControllers/helpers/pages.js +2 -1
- package/.server/server/plugins/engine/pageControllers/helpers/pages.js.map +1 -1
- package/.server/server/plugins/engine/views/components/_location-field-base.html +1 -0
- package/.server/server/plugins/engine/views/components/geospatialfield.html +1 -0
- package/.server/server/plugins/engine/views/components/osgridreffield.html +3 -1
- package/package.json +1 -1
- package/src/client/javascripts/geospatial-map.js +169 -47
- package/src/client/javascripts/location-map.js +50 -2
- package/src/client/javascripts/map-constants.js +3 -0
- package/src/client/javascripts/map.js +5 -0
- package/src/server/plugins/engine/__stubs__/definitions.ts +155 -0
- package/src/server/plugins/engine/components/GeospatialField.ts +1 -0
- package/src/server/plugins/engine/components/LocationFieldBase.ts +4 -0
- package/src/server/plugins/engine/components/LocationFieldHelpers.ts +2 -1
- package/src/server/plugins/engine/components/helpers/components.ts +2 -1
- package/src/server/plugins/engine/errors.ts +78 -0
- package/src/server/plugins/engine/i18n/translations/cy.json +51 -1
- package/src/server/plugins/engine/i18n/translations/en-GB.json +51 -1
- package/src/server/plugins/engine/models/FormModel.ts +12 -2
- package/src/server/plugins/engine/pageControllers/helpers/pages.ts +2 -1
- package/src/server/plugins/engine/views/components/_location-field-base.html +1 -0
- package/src/server/plugins/engine/views/components/geospatialfield.html +1 -0
- package/src/server/plugins/engine/views/components/osgridreffield.html +3 -1
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ComponentType,
|
|
3
|
+
ConditionType,
|
|
4
|
+
ControllerType,
|
|
5
|
+
Engine,
|
|
6
|
+
OperatorName,
|
|
7
|
+
SchemaVersion,
|
|
8
|
+
type FormDefinition,
|
|
9
|
+
type PageQuestion
|
|
10
|
+
} from '@defra/forms-model'
|
|
11
|
+
|
|
12
|
+
const YES_NO_COMPONENT_ID = 'a7c0242f-2a31-45b2-8c71-ff2ac7f53288'
|
|
13
|
+
const USER_TYPE_LIST_ID = '4fa26e9c-07cf-47cd-a9dd-5cec0dd3f544'
|
|
14
|
+
const EXISTING_USER_ITEM_ID = '55fe0067-d011-4d33-886c-e1aa266637c3'
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* A minimal, schema-valid V2 definition: one YesNo question page and a
|
|
18
|
+
* summary page. Each call returns a fresh object, so tests can mutate their
|
|
19
|
+
* copy freely without affecting each other.
|
|
20
|
+
*/
|
|
21
|
+
export function buildDefinition(): FormDefinition {
|
|
22
|
+
return {
|
|
23
|
+
name: 'Stub definition',
|
|
24
|
+
engine: Engine.V2,
|
|
25
|
+
schema: SchemaVersion.V2,
|
|
26
|
+
startPage: '/summary',
|
|
27
|
+
pages: [
|
|
28
|
+
{
|
|
29
|
+
id: '449c053b-9201-4312-9a75-187afc6ba48b',
|
|
30
|
+
path: '/licence',
|
|
31
|
+
title: 'Licence',
|
|
32
|
+
components: [
|
|
33
|
+
{
|
|
34
|
+
id: YES_NO_COMPONENT_ID,
|
|
35
|
+
name: 'xVrYaJ',
|
|
36
|
+
type: ComponentType.YesNoField,
|
|
37
|
+
title: 'Do you have a licence?',
|
|
38
|
+
shortDescription: 'Licence',
|
|
39
|
+
options: { required: true }
|
|
40
|
+
}
|
|
41
|
+
],
|
|
42
|
+
next: []
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
id: '449c053b-9201-4312-9a75-187afc6ba48c',
|
|
46
|
+
path: '/summary',
|
|
47
|
+
title: 'Summary',
|
|
48
|
+
controller: ControllerType.Summary,
|
|
49
|
+
components: []
|
|
50
|
+
}
|
|
51
|
+
],
|
|
52
|
+
lists: [],
|
|
53
|
+
sections: [],
|
|
54
|
+
conditions: []
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* A definition that passes schema validation but cannot be used by the
|
|
60
|
+
* engine: a ListItemRef condition points at the YesNoField (boolean), so the
|
|
61
|
+
* condition expression builder produces an unparseable expression. Distilled
|
|
62
|
+
* from a real production incident.
|
|
63
|
+
*/
|
|
64
|
+
export function buildBrokenConditionDefinition(): FormDefinition {
|
|
65
|
+
const definition = buildDefinition()
|
|
66
|
+
|
|
67
|
+
definition.name = 'Broken condition fixture'
|
|
68
|
+
|
|
69
|
+
const questionPage = definition.pages[0] as PageQuestion
|
|
70
|
+
// A YesNoField cannot legitimately carry a custom list — that is the
|
|
71
|
+
// corruption this fixture models — so the TS component types (rightly)
|
|
72
|
+
// have no `list` property here and a cast is required.
|
|
73
|
+
;(questionPage.components[0] as { list?: string }).list = USER_TYPE_LIST_ID
|
|
74
|
+
definition.lists = [
|
|
75
|
+
{
|
|
76
|
+
id: USER_TYPE_LIST_ID,
|
|
77
|
+
name: 'XtfRYR',
|
|
78
|
+
title: 'User type list',
|
|
79
|
+
type: 'string',
|
|
80
|
+
items: [
|
|
81
|
+
{
|
|
82
|
+
id: EXISTING_USER_ITEM_ID,
|
|
83
|
+
text: 'existing user',
|
|
84
|
+
value: 'existing user'
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
id: '2277c7e5-7fef-46c6-993b-d294116d6d6b',
|
|
88
|
+
text: 'new user',
|
|
89
|
+
value: 'new user'
|
|
90
|
+
}
|
|
91
|
+
]
|
|
92
|
+
}
|
|
93
|
+
]
|
|
94
|
+
definition.conditions = [
|
|
95
|
+
{
|
|
96
|
+
id: '3f9d3a35-6dee-4706-806c-3f776129f631',
|
|
97
|
+
displayName: 'Existing user',
|
|
98
|
+
items: [
|
|
99
|
+
{
|
|
100
|
+
id: '7d7f58ee-c860-4d24-8a13-de5cb9af53d8',
|
|
101
|
+
componentId: YES_NO_COMPONENT_ID,
|
|
102
|
+
operator: OperatorName.Is,
|
|
103
|
+
type: ConditionType.ListItemRef,
|
|
104
|
+
value: {
|
|
105
|
+
listId: USER_TYPE_LIST_ID,
|
|
106
|
+
itemId: [EXISTING_USER_ITEM_ID]
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
]
|
|
110
|
+
}
|
|
111
|
+
]
|
|
112
|
+
|
|
113
|
+
return definition
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* A schema-valid definition whose first page names a controller that is
|
|
118
|
+
* neither built in nor registered by the host application.
|
|
119
|
+
*/
|
|
120
|
+
export function buildUnknownControllerDefinition(): FormDefinition {
|
|
121
|
+
const definition = buildDefinition()
|
|
122
|
+
|
|
123
|
+
definition.name = 'Unknown controller fixture'
|
|
124
|
+
definition.pages[0].controller =
|
|
125
|
+
'NoSuchPageController' as unknown as ControllerType
|
|
126
|
+
|
|
127
|
+
return definition
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Schema-valid (component types are free strings in the schema), but the
|
|
132
|
+
* question uses a type the engine has no component class for.
|
|
133
|
+
*/
|
|
134
|
+
export function buildUnknownComponentDefinition(): FormDefinition {
|
|
135
|
+
const definition = buildDefinition()
|
|
136
|
+
|
|
137
|
+
definition.name = 'Unknown component fixture'
|
|
138
|
+
const questionPage = definition.pages[0] as PageQuestion
|
|
139
|
+
questionPage.components[0].type = 'MyUnknownField' as unknown as ComponentType
|
|
140
|
+
|
|
141
|
+
return definition
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* A definition that fails schema validation: the question page appears
|
|
146
|
+
* twice, violating the pages uniqueness rule.
|
|
147
|
+
*/
|
|
148
|
+
export function buildSchemaInvalidDefinition(): FormDefinition {
|
|
149
|
+
const definition = buildDefinition()
|
|
150
|
+
|
|
151
|
+
definition.name = 'Schema invalid fixture'
|
|
152
|
+
definition.pages.push(buildDefinition().pages[0])
|
|
153
|
+
|
|
154
|
+
return definition
|
|
155
|
+
}
|
|
@@ -124,6 +124,10 @@ export abstract class LocationFieldBase extends FormComponent {
|
|
|
124
124
|
getViewModel(context: RenderContext) {
|
|
125
125
|
const viewModel = super.getViewModel(context)
|
|
126
126
|
|
|
127
|
+
Object.assign(viewModel, {
|
|
128
|
+
lang: context.translator.language
|
|
129
|
+
})
|
|
130
|
+
|
|
127
131
|
if (this.instructionText) {
|
|
128
132
|
const { tComponent } = context.translator
|
|
129
133
|
return {
|
|
@@ -194,7 +194,8 @@ export function getLocationFieldViewModel(
|
|
|
194
194
|
country:
|
|
195
195
|
component instanceof EastingNorthingField
|
|
196
196
|
? component.options.countries?.at(0)
|
|
197
|
-
: undefined
|
|
197
|
+
: undefined,
|
|
198
|
+
lang: context.translator.language
|
|
198
199
|
}
|
|
199
200
|
|
|
200
201
|
if (component.options.instructionText) {
|
|
@@ -6,6 +6,7 @@ import { ListFormComponent } from '../ListFormComponent.js'
|
|
|
6
6
|
import { escapeMarkdown } from './index.js'
|
|
7
7
|
import * as Components from '../index.js'
|
|
8
8
|
import { markdown } from '../markdownParser.js'
|
|
9
|
+
import { UnknownComponentTypeError } from '../../errors.js'
|
|
9
10
|
import { type Translator } from '../../i18n/types.js'
|
|
10
11
|
import { type FormState } from '../../types.js'
|
|
11
12
|
|
|
@@ -210,7 +211,7 @@ export function createComponent(
|
|
|
210
211
|
}
|
|
211
212
|
|
|
212
213
|
if (typeof component === 'undefined') {
|
|
213
|
-
throw new
|
|
214
|
+
throw new UnknownComponentTypeError(def.type)
|
|
214
215
|
}
|
|
215
216
|
|
|
216
217
|
return component
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { type ValidationError } from 'joi'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Base class for errors caused by an invalid form definition — whether it
|
|
5
|
+
* fails schema validation or passes the schema but cannot be used by the
|
|
6
|
+
* engine. Never thrown directly — throw a subclass. Consumers (e.g.
|
|
7
|
+
* forms-runner error pages) detect the family with
|
|
8
|
+
* `instanceof InvalidFormDefinitionError`.
|
|
9
|
+
*
|
|
10
|
+
* When subclassing, remember that the message and the messages of any
|
|
11
|
+
* `cause` chain are shown to form authors on preview error pages. Only
|
|
12
|
+
* describe the problem with the form itself — never include sensitive
|
|
13
|
+
* details such as file paths, configuration values or secrets.
|
|
14
|
+
*/
|
|
15
|
+
export class InvalidFormDefinitionError extends Error {
|
|
16
|
+
constructor(message: string, options?: ErrorOptions) {
|
|
17
|
+
super(message, options)
|
|
18
|
+
this.name = 'InvalidFormDefinitionError'
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Thrown when a condition in the definition cannot be compiled into an
|
|
24
|
+
* evaluatable expression (e.g. it references the wrong component or list).
|
|
25
|
+
*/
|
|
26
|
+
export class ConditionBuildError extends InvalidFormDefinitionError {
|
|
27
|
+
public readonly conditionName: string
|
|
28
|
+
|
|
29
|
+
constructor(conditionName: string, options?: ErrorOptions) {
|
|
30
|
+
super(`Failed to build condition '${conditionName}'`, options)
|
|
31
|
+
this.name = 'ConditionBuildError'
|
|
32
|
+
this.conditionName = conditionName
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Thrown when a page names a controller that is neither built in nor
|
|
38
|
+
* registered by the host application.
|
|
39
|
+
*/
|
|
40
|
+
export class UnknownPageControllerError extends InvalidFormDefinitionError {
|
|
41
|
+
public readonly controllerName: string
|
|
42
|
+
|
|
43
|
+
constructor(controllerName: string) {
|
|
44
|
+
super(`Page controller ${controllerName} does not exist`)
|
|
45
|
+
this.name = 'UnknownPageControllerError'
|
|
46
|
+
this.controllerName = controllerName
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Thrown when a component declares a type with no registered implementation.
|
|
52
|
+
*/
|
|
53
|
+
export class UnknownComponentTypeError extends InvalidFormDefinitionError {
|
|
54
|
+
public readonly componentType: string
|
|
55
|
+
|
|
56
|
+
constructor(componentType: string) {
|
|
57
|
+
super(`Component type ${componentType} does not exist`)
|
|
58
|
+
this.name = 'UnknownComponentTypeError'
|
|
59
|
+
this.componentType = componentType
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Thrown when a form definition fails Joi schema validation. The raw
|
|
65
|
+
* ValidationError is preserved as `cause` so consumers can reach the
|
|
66
|
+
* per-field details; the message carries Joi's own summary so log lines
|
|
67
|
+
* remain diagnostic for consumers that only read `error.message`.
|
|
68
|
+
*/
|
|
69
|
+
export class SchemaValidationError extends InvalidFormDefinitionError {
|
|
70
|
+
// Error types `cause` as unknown; this constructor only accepts a Joi
|
|
71
|
+
// ValidationError, so narrow the declaration for consumers (type-only).
|
|
72
|
+
declare cause: ValidationError
|
|
73
|
+
|
|
74
|
+
constructor(cause: ValidationError) {
|
|
75
|
+
super(`Invalid form definition: ${cause.message}`, { cause })
|
|
76
|
+
this.name = 'SchemaValidationError'
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -323,7 +323,16 @@
|
|
|
323
323
|
},
|
|
324
324
|
|
|
325
325
|
"locationFieldBase": {
|
|
326
|
-
"howToFind": "Sut i ddod o hyd i fanylion lleoliad"
|
|
326
|
+
"howToFind": "Sut i ddod o hyd i fanylion lleoliad",
|
|
327
|
+
"map": {
|
|
328
|
+
"helpPanel": {
|
|
329
|
+
"label": "Sut i ddefnyddio'r map hwn",
|
|
330
|
+
"point1": "Chwilio am sir, lle neu god post",
|
|
331
|
+
"point2": "Defnyddiwch yr eiconau + a - i chwyddo i mewn ac allan",
|
|
332
|
+
"point3": "Defnyddiwch lyfliwr neu allweddlon i ganolbwyntio'r pwynt yn y lleoliad",
|
|
333
|
+
"point4": "Cliciwch i ychwanegu'r lleoliad i'r map"
|
|
334
|
+
}
|
|
335
|
+
}
|
|
327
336
|
},
|
|
328
337
|
|
|
329
338
|
"yesNoField": {
|
|
@@ -350,6 +359,47 @@
|
|
|
350
359
|
"added_few": "Ychwanegwyd [[count]] lleoliad",
|
|
351
360
|
"added_many": "Ychwanegwyd [[count]] o leoliadau",
|
|
352
361
|
"added_other": "Ychwanegwyd [[count]] lleoliad",
|
|
362
|
+
"map": {
|
|
363
|
+
"helpPanel": {
|
|
364
|
+
"label": "Sut i ddefnyddio'r map hwn",
|
|
365
|
+
"ledeIntro": "Gallwch ychwanegu",
|
|
366
|
+
"ledeOutro": "i'r map.",
|
|
367
|
+
"lineText": "llinell",
|
|
368
|
+
"shapeText": "siâp",
|
|
369
|
+
"lineOrShapeText": "llinell neu siâp",
|
|
370
|
+
"typesPhrasePoint": "pwyntiau",
|
|
371
|
+
"typesPhraseLine": "lliniau",
|
|
372
|
+
"typesPhraseShape": "siâpiau",
|
|
373
|
+
"typesPhraseOr": "neu",
|
|
374
|
+
"point1": "Chwilio am sir, lle neu god post",
|
|
375
|
+
"point2": "Defnyddiwch yr eiconau + a - i chwyddo i mewn ac allan",
|
|
376
|
+
"point3": "Cliciwch ddwywaith, neu dewiswch 'Wedi gorffen', pan fyddwch wedi",
|
|
377
|
+
"point4": "Rhowch enw i'r lleoliad"
|
|
378
|
+
},
|
|
379
|
+
"typeDescriptions": {
|
|
380
|
+
"point": "Pwynt",
|
|
381
|
+
"line": "Llinell",
|
|
382
|
+
"polygon": "Siâp"
|
|
383
|
+
},
|
|
384
|
+
"buttons": {
|
|
385
|
+
"point": "Ychwanegu pwynt",
|
|
386
|
+
"line": "Ychwanegu llinell",
|
|
387
|
+
"polygon": "Ychwanegu siâp"
|
|
388
|
+
},
|
|
389
|
+
"itemLabel1": "Lleoliad",
|
|
390
|
+
"itemLabel2": "disgrifiad",
|
|
391
|
+
"itemUpdateLink": "Diweddaru",
|
|
392
|
+
"itemDeleteLink": "Dileu",
|
|
393
|
+
"itemFocusLink": "Dangos",
|
|
394
|
+
"itemVisuallyHidden": "lleoliad",
|
|
395
|
+
"details": {
|
|
396
|
+
"summary": "Cyfesurynnau",
|
|
397
|
+
"type": "Math",
|
|
398
|
+
"centroidGridReference": "Canol cyfeirnod grid",
|
|
399
|
+
"firstPointGridReference": "Cyfeirnod grid y pwynt cyntaf",
|
|
400
|
+
"detailedCoordinates": "Cyfesurynnau manwl"
|
|
401
|
+
}
|
|
402
|
+
},
|
|
353
403
|
"validation": {
|
|
354
404
|
"descriptionRequired": "Rhowch ddisgrifiad ar gyfer lleoliad [[count]]",
|
|
355
405
|
"wrongCountry": "Rhaid i leoliad [[count]] fod y tu mewn i [[country]]"
|
|
@@ -266,7 +266,16 @@
|
|
|
266
266
|
},
|
|
267
267
|
|
|
268
268
|
"locationFieldBase": {
|
|
269
|
-
"howToFind": "How to find location details"
|
|
269
|
+
"howToFind": "How to find location details",
|
|
270
|
+
"map": {
|
|
271
|
+
"helpPanel": {
|
|
272
|
+
"label": "How to use this map",
|
|
273
|
+
"point1": "Search for a place or postcode",
|
|
274
|
+
"point2": "Use the + and - icons to zoom in and out",
|
|
275
|
+
"point3": "Use a mouse or keyboard to centre the point at the location",
|
|
276
|
+
"point4": "Click to add the location to the map"
|
|
277
|
+
}
|
|
278
|
+
}
|
|
270
279
|
},
|
|
271
280
|
|
|
272
281
|
"yesNoField": {
|
|
@@ -289,6 +298,47 @@
|
|
|
289
298
|
"geospatialField": {
|
|
290
299
|
"added_one": "Added [[count]] location",
|
|
291
300
|
"added_other": "Added [[count]] locations",
|
|
301
|
+
"map": {
|
|
302
|
+
"helpPanel": {
|
|
303
|
+
"label": "How to use this map",
|
|
304
|
+
"ledeIntro": "You can add",
|
|
305
|
+
"ledeOutro": "to the map.",
|
|
306
|
+
"lineText": "a line",
|
|
307
|
+
"shapeText": "a shape",
|
|
308
|
+
"lineOrShapeText": "a line or shape",
|
|
309
|
+
"typesPhrasePoint": "points",
|
|
310
|
+
"typesPhraseLine": "lines",
|
|
311
|
+
"typesPhraseShape": "shapes",
|
|
312
|
+
"typesPhraseOr": "or",
|
|
313
|
+
"point1": "Search for a county, place or postcode",
|
|
314
|
+
"point2": "Use the + and - icons to zoom in and out",
|
|
315
|
+
"point3": "Double‑click, or select 'Done', when you have finished drawing",
|
|
316
|
+
"point4": "Give the location a name"
|
|
317
|
+
},
|
|
318
|
+
"typeDescriptions": {
|
|
319
|
+
"point": "Point",
|
|
320
|
+
"line": "Line",
|
|
321
|
+
"polygon": "Shape"
|
|
322
|
+
},
|
|
323
|
+
"buttons": {
|
|
324
|
+
"point": "Add point",
|
|
325
|
+
"line": "Add line",
|
|
326
|
+
"polygon": "Add shape"
|
|
327
|
+
},
|
|
328
|
+
"itemLabel1": "Location",
|
|
329
|
+
"itemLabel2": "description",
|
|
330
|
+
"itemUpdateLink": "Update",
|
|
331
|
+
"itemDeleteLink": "Delete",
|
|
332
|
+
"itemFocusLink": "Show",
|
|
333
|
+
"itemVisuallyHidden": "location",
|
|
334
|
+
"details": {
|
|
335
|
+
"summary": "Coordinates",
|
|
336
|
+
"type": "Type",
|
|
337
|
+
"centroidGridReference": "Centre grid reference",
|
|
338
|
+
"firstPointGridReference": "First point grid reference",
|
|
339
|
+
"detailedCoordinates": "Detailed coordinates"
|
|
340
|
+
}
|
|
341
|
+
},
|
|
292
342
|
"validation": {
|
|
293
343
|
"descriptionRequired": "Enter description for location [[count]]",
|
|
294
344
|
"wrongCountry": "Location [[count]] must be within [[country]]"
|
|
@@ -39,6 +39,10 @@ import {
|
|
|
39
39
|
type Component
|
|
40
40
|
} from '../components/helpers/components.js'
|
|
41
41
|
import { todayAsDateOnly } from '../date-helper.js'
|
|
42
|
+
import {
|
|
43
|
+
ConditionBuildError,
|
|
44
|
+
SchemaValidationError
|
|
45
|
+
} from '../errors.js'
|
|
42
46
|
import {
|
|
43
47
|
findPage,
|
|
44
48
|
getError,
|
|
@@ -124,7 +128,7 @@ export class FormModel {
|
|
|
124
128
|
const result = schema.validate(def, { abortEarly: false })
|
|
125
129
|
|
|
126
130
|
if (result.error) {
|
|
127
|
-
throw result.error
|
|
131
|
+
throw new SchemaValidationError(result.error)
|
|
128
132
|
}
|
|
129
133
|
|
|
130
134
|
// Make a clone of the shallow copy returned
|
|
@@ -292,7 +296,13 @@ export class FormModel {
|
|
|
292
296
|
})
|
|
293
297
|
|
|
294
298
|
const { name, displayName, value } = condition
|
|
295
|
-
|
|
299
|
+
|
|
300
|
+
let expr
|
|
301
|
+
try {
|
|
302
|
+
expr = this.toConditionExpression(value, parser)
|
|
303
|
+
} catch (cause) {
|
|
304
|
+
throw new ConditionBuildError(displayName, { cause })
|
|
305
|
+
}
|
|
296
306
|
|
|
297
307
|
const fn = (evaluationState: FormState) => {
|
|
298
308
|
const ctx = this.toConditionContext(evaluationState, this.conditions)
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
type Page
|
|
5
5
|
} from '@defra/forms-model'
|
|
6
6
|
|
|
7
|
+
import { UnknownPageControllerError } from '../../errors.js'
|
|
7
8
|
import { type FormModel } from '../../models/index.js'
|
|
8
9
|
import * as PageControllers from '../index.js'
|
|
9
10
|
|
|
@@ -66,7 +67,7 @@ export function createPage(model: FormModel, pageDef: Page) {
|
|
|
66
67
|
}
|
|
67
68
|
|
|
68
69
|
if (typeof controller === 'undefined') {
|
|
69
|
-
throw new
|
|
70
|
+
throw new UnknownPageControllerError(pageDef.controller)
|
|
70
71
|
}
|
|
71
72
|
|
|
72
73
|
return controller
|
|
@@ -73,6 +73,7 @@
|
|
|
73
73
|
<div id="{{component.model.name}}" class="govuk-form-group app-location-field {{ "govuk-form-group--error" if hasErrors }}"
|
|
74
74
|
data-locationtype="{{component.type | lower}}"
|
|
75
75
|
data-country="{{component.model.country}}"
|
|
76
|
+
data-lang="{{component.model.lang}}"
|
|
76
77
|
data-maplayers="{{component.model.mapLayers}}">
|
|
77
78
|
{{ govukFieldset({
|
|
78
79
|
legend: {
|
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
|
|
4
4
|
{% macro OsGridRefField(component) %}
|
|
5
5
|
{% set hasErrors = component.model.errorMessage %}
|
|
6
|
-
<div class="govuk-form-group app-location-field {{ "govuk-form-group--error" if hasErrors }}"
|
|
6
|
+
<div class="govuk-form-group app-location-field {{ "govuk-form-group--error" if hasErrors }}"
|
|
7
|
+
data-locationtype="{{component.type | lower}}"
|
|
8
|
+
data-lang="{{component.model.lang}}">
|
|
7
9
|
<div class="app-location-field-inputs">
|
|
8
10
|
{{ TextField(component) }}
|
|
9
11
|
</div>
|