@defra/forms-engine-plugin 4.13.0 → 4.14.1

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 (32) hide show
  1. package/.public/javascripts/shared.min.js +1 -1
  2. package/.public/javascripts/shared.min.js.map +1 -1
  3. package/.server/client/javascripts/geospatial-map.d.ts +32 -0
  4. package/.server/client/javascripts/geospatial-map.js +161 -70
  5. package/.server/client/javascripts/geospatial-map.js.map +1 -1
  6. package/.server/client/javascripts/map.d.ts +6 -0
  7. package/.server/client/javascripts/map.js +5 -0
  8. package/.server/client/javascripts/map.js.map +1 -1
  9. package/.server/client/javascripts/utils.d.ts +7 -0
  10. package/.server/client/javascripts/utils.js +21 -0
  11. package/.server/client/javascripts/utils.js.map +1 -0
  12. package/.server/server/forms/simple-form.yaml +9 -0
  13. package/.server/server/plugins/engine/components/GeospatialField.d.ts +1 -0
  14. package/.server/server/plugins/engine/components/GeospatialField.js +9 -5
  15. package/.server/server/plugins/engine/components/GeospatialField.js.map +1 -1
  16. package/.server/server/plugins/engine/components/helpers/geospatial.d.ts +2 -2
  17. package/.server/server/plugins/engine/components/helpers/geospatial.js +32 -5
  18. package/.server/server/plugins/engine/components/helpers/geospatial.js.map +1 -1
  19. package/.server/server/plugins/engine/components/helpers/geospatial.test.js +37 -6
  20. package/.server/server/plugins/engine/components/helpers/geospatial.test.js.map +1 -1
  21. package/.server/server/plugins/engine/pageControllers/validationOptions.js +4 -1
  22. package/.server/server/plugins/engine/pageControllers/validationOptions.js.map +1 -1
  23. package/.server/server/plugins/engine/views/components/geospatialfield.html +1 -1
  24. package/package.json +2 -2
  25. package/src/client/javascripts/geospatial-map.js +168 -53
  26. package/src/client/javascripts/map.js +5 -0
  27. package/src/client/javascripts/utils.js +23 -0
  28. package/src/server/forms/simple-form.yaml +9 -0
  29. package/src/server/plugins/engine/components/GeospatialField.ts +11 -7
  30. package/src/server/plugins/engine/components/helpers/geospatial.ts +49 -11
  31. package/src/server/plugins/engine/pageControllers/validationOptions.ts +4 -1
  32. package/src/server/plugins/engine/views/components/geospatialfield.html +1 -1
@@ -19,7 +19,8 @@ const Joi = JoiBase.extend({
19
19
  from: 'string',
20
20
  method(value, helpers) {
21
21
  if (typeof value === 'string') {
22
- if (value.trim() === '') {
22
+ const trimmed = value.trim();
23
+ if (trimmed === '' || trimmed === '[]') {
23
24
  return {
24
25
  value: undefined
25
26
  };
@@ -72,10 +73,36 @@ const featureSchema = Joi.object().keys({
72
73
  properties: featurePropertiesSchema,
73
74
  geometry: featureGeometrySchema
74
75
  });
75
- const geospatialSchema = Joi.array().items(featureSchema).unique('id').required();
76
- export function getGeospatialSchema(country) {
76
+ function applySchemaConstraints(schema, def) {
77
+ const {
78
+ options,
79
+ schema: constraints
80
+ } = def;
81
+ const isOptional = options.required === false;
82
+ if (typeof constraints?.length === 'number') {
83
+ schema = schema.length(constraints.length);
84
+ } else {
85
+ if (typeof constraints?.min === 'number') {
86
+ schema = schema.min(constraints.min);
87
+ } else if (!isOptional) {
88
+ schema = schema.min(1);
89
+ }
90
+ schema = schema.max(typeof constraints?.max === 'number' ? constraints.max : 50);
91
+ }
92
+ if (isOptional) {
93
+ schema = schema.optional();
94
+ } else {
95
+ schema = schema.required();
96
+ }
97
+ return schema;
98
+ }
99
+ export function getGeospatialSchema(def) {
100
+ const {
101
+ options = {}
102
+ } = def;
103
+ const country = options.countries?.at(0);
77
104
  if (!country) {
78
- return geospatialSchema;
105
+ return applySchemaConstraints(Joi.array().items(featureSchema).unique('id'), def);
79
106
  }
80
107
  const validateCountryBounds = (value, helpers) => {
81
108
  const countryFeature = countries.features.find(feature => feature.id === country);
@@ -93,7 +120,7 @@ export function getGeospatialSchema(country) {
93
120
  // eslint-disable-next-line @typescript-eslint/no-unsafe-return
94
121
  return value;
95
122
  };
96
- return Joi.array().items(featureSchema.custom(validateCountryBounds)).unique('id').required();
123
+ return applySchemaConstraints(Joi.array().items(featureSchema.custom(validateCountryBounds)).unique('id'), def);
97
124
  }
98
125
 
99
126
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"geospatial.js","names":["GeospatialFieldOptionsCountryEnum","Bourne","booleanWithin","JoiBase","countries","countriesDesc","England","NorthernIreland","Scotland","Wales","Joi","extend","type","base","array","messages","coerce","from","method","value","helpers","trim","undefined","parse","result","errors","error","coordinatesSchema","items","number","required","featurePropertiesSchema","object","keys","description","string","coordinateGridReference","centroidGridReference","featureGeometrySchema","valid","coordinates","when","switch","is","then","min","featureSchema","id","properties","geometry","geospatialSchema","unique","getGeospatialSchema","country","validateCountryBounds","countryFeature","features","find","feature","custom"],"sources":["../../../../../../src/server/plugins/engine/components/helpers/geospatial.ts"],"sourcesContent":["import {\n GeospatialFieldOptionsCountryEnum,\n type GeospatialFieldOptionsCountry\n} from '@defra/forms-model'\nimport Bourne from '@hapi/bourne'\nimport { booleanWithin } from '@turf/boolean-within'\nimport JoiBase, { type CustomValidator } from 'joi'\n\nimport {\n type Coordinates,\n type Feature,\n type FeatureProperties,\n type Geometry\n} from '~/src/server/plugins/engine/types.js'\nimport { countries } from '~/src/server/plugins/map/routes/index.js'\n\nconst countriesDesc: Record<GeospatialFieldOptionsCountryEnum, string> = {\n [GeospatialFieldOptionsCountryEnum.England]: 'England',\n [GeospatialFieldOptionsCountryEnum.NorthernIreland]: 'Northern Ireland',\n [GeospatialFieldOptionsCountryEnum.Scotland]: 'Scotland',\n [GeospatialFieldOptionsCountryEnum.Wales]: 'Wales'\n}\n\nconst Joi = JoiBase.extend({\n type: 'array',\n base: JoiBase.array(),\n messages: {\n 'object.invalidjson': '{{#label}} must be a valid json array string'\n },\n coerce: {\n from: 'string',\n method(value, helpers) {\n if (typeof value === 'string') {\n if (value.trim() === '') {\n return {\n value: undefined\n }\n }\n\n try {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n return { value: Bourne.parse(value) }\n } catch {\n const result = {\n value,\n errors: [helpers.error('object.invalidjson')]\n }\n\n return result\n }\n } else {\n return {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n value\n }\n }\n }\n }\n}) as JoiBase.Root\n\nconst coordinatesSchema = Joi.array<Coordinates[]>()\n .items(Joi.number().required(), Joi.number().required())\n .required()\n\nconst featurePropertiesSchema = Joi.object<FeatureProperties>()\n .keys({\n description: Joi.string().required(),\n coordinateGridReference: Joi.string().required(),\n centroidGridReference: Joi.string().required()\n })\n .required()\n\nconst featureGeometrySchema = Joi.object<Geometry>().keys({\n type: Joi.string().valid('Point', 'LineString', 'Polygon').required(),\n coordinates: Joi.array()\n .when('type', {\n switch: [\n { is: 'Point', then: coordinatesSchema },\n {\n is: 'LineString',\n then: Joi.array().items(coordinatesSchema).min(2)\n },\n {\n is: 'Polygon',\n then: Joi.array().items(Joi.array().items(coordinatesSchema).min(3))\n }\n ]\n })\n .required()\n})\n\nconst featureSchema = Joi.object<Feature>().keys({\n id: Joi.string().required(),\n type: Joi.string().valid('Feature').required(),\n properties: featurePropertiesSchema,\n geometry: featureGeometrySchema\n})\n\nconst geospatialSchema = Joi.array<Feature[]>()\n .items(featureSchema)\n .unique('id')\n .required()\n\nexport function getGeospatialSchema(country?: GeospatialFieldOptionsCountry) {\n if (!country) {\n return geospatialSchema\n }\n\n const validateCountryBounds: CustomValidator = (value, helpers) => {\n const countryFeature = countries.features.find(\n (feature) => feature.id === country\n )\n\n if (!countryFeature) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return value\n }\n\n const result = booleanWithin(value as Geometry | Feature, countryFeature)\n\n if (!result) {\n return helpers.error('any.custom', {\n country: countriesDesc[country as GeospatialFieldOptionsCountryEnum]\n })\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return value\n }\n\n return Joi.array<Feature[]>()\n .items(featureSchema.custom(validateCountryBounds))\n .unique('id')\n .required()\n}\n\n/**\n * @import { CustomHelpers } from 'joi'\n */\n"],"mappings":"AAAA,SACEA,iCAAiC,QAE5B,oBAAoB;AAC3B,OAAOC,MAAM,MAAM,cAAc;AACjC,SAASC,aAAa,QAAQ,sBAAsB;AACpD,OAAOC,OAAO,MAAgC,KAAK;AAQnD,SAASC,SAAS;AAElB,MAAMC,aAAgE,GAAG;EACvE,CAACL,iCAAiC,CAACM,OAAO,GAAG,SAAS;EACtD,CAACN,iCAAiC,CAACO,eAAe,GAAG,kBAAkB;EACvE,CAACP,iCAAiC,CAACQ,QAAQ,GAAG,UAAU;EACxD,CAACR,iCAAiC,CAACS,KAAK,GAAG;AAC7C,CAAC;AAED,MAAMC,GAAG,GAAGP,OAAO,CAACQ,MAAM,CAAC;EACzBC,IAAI,EAAE,OAAO;EACbC,IAAI,EAAEV,OAAO,CAACW,KAAK,CAAC,CAAC;EACrBC,QAAQ,EAAE;IACR,oBAAoB,EAAE;EACxB,CAAC;EACDC,MAAM,EAAE;IACNC,IAAI,EAAE,QAAQ;IACdC,MAAMA,CAACC,KAAK,EAAEC,OAAO,EAAE;MACrB,IAAI,OAAOD,KAAK,KAAK,QAAQ,EAAE;QAC7B,IAAIA,KAAK,CAACE,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE;UACvB,OAAO;YACLF,KAAK,EAAEG;UACT,CAAC;QACH;QAEA,IAAI;UACF;UACA,OAAO;YAAEH,KAAK,EAAElB,MAAM,CAACsB,KAAK,CAACJ,KAAK;UAAE,CAAC;QACvC,CAAC,CAAC,MAAM;UACN,MAAMK,MAAM,GAAG;YACbL,KAAK;YACLM,MAAM,EAAE,CAACL,OAAO,CAACM,KAAK,CAAC,oBAAoB,CAAC;UAC9C,CAAC;UAED,OAAOF,MAAM;QACf;MACF,CAAC,MAAM;QACL,OAAO;UACL;UACAL;QACF,CAAC;MACH;IACF;EACF;AACF,CAAC,CAAiB;AAElB,MAAMQ,iBAAiB,GAAGjB,GAAG,CAACI,KAAK,CAAgB,CAAC,CACjDc,KAAK,CAAClB,GAAG,CAACmB,MAAM,CAAC,CAAC,CAACC,QAAQ,CAAC,CAAC,EAAEpB,GAAG,CAACmB,MAAM,CAAC,CAAC,CAACC,QAAQ,CAAC,CAAC,CAAC,CACvDA,QAAQ,CAAC,CAAC;AAEb,MAAMC,uBAAuB,GAAGrB,GAAG,CAACsB,MAAM,CAAoB,CAAC,CAC5DC,IAAI,CAAC;EACJC,WAAW,EAAExB,GAAG,CAACyB,MAAM,CAAC,CAAC,CAACL,QAAQ,CAAC,CAAC;EACpCM,uBAAuB,EAAE1B,GAAG,CAACyB,MAAM,CAAC,CAAC,CAACL,QAAQ,CAAC,CAAC;EAChDO,qBAAqB,EAAE3B,GAAG,CAACyB,MAAM,CAAC,CAAC,CAACL,QAAQ,CAAC;AAC/C,CAAC,CAAC,CACDA,QAAQ,CAAC,CAAC;AAEb,MAAMQ,qBAAqB,GAAG5B,GAAG,CAACsB,MAAM,CAAW,CAAC,CAACC,IAAI,CAAC;EACxDrB,IAAI,EAAEF,GAAG,CAACyB,MAAM,CAAC,CAAC,CAACI,KAAK,CAAC,OAAO,EAAE,YAAY,EAAE,SAAS,CAAC,CAACT,QAAQ,CAAC,CAAC;EACrEU,WAAW,EAAE9B,GAAG,CAACI,KAAK,CAAC,CAAC,CACrB2B,IAAI,CAAC,MAAM,EAAE;IACZC,MAAM,EAAE,CACN;MAAEC,EAAE,EAAE,OAAO;MAAEC,IAAI,EAAEjB;IAAkB,CAAC,EACxC;MACEgB,EAAE,EAAE,YAAY;MAChBC,IAAI,EAAElC,GAAG,CAACI,KAAK,CAAC,CAAC,CAACc,KAAK,CAACD,iBAAiB,CAAC,CAACkB,GAAG,CAAC,CAAC;IAClD,CAAC,EACD;MACEF,EAAE,EAAE,SAAS;MACbC,IAAI,EAAElC,GAAG,CAACI,KAAK,CAAC,CAAC,CAACc,KAAK,CAAClB,GAAG,CAACI,KAAK,CAAC,CAAC,CAACc,KAAK,CAACD,iBAAiB,CAAC,CAACkB,GAAG,CAAC,CAAC,CAAC;IACrE,CAAC;EAEL,CAAC,CAAC,CACDf,QAAQ,CAAC;AACd,CAAC,CAAC;AAEF,MAAMgB,aAAa,GAAGpC,GAAG,CAACsB,MAAM,CAAU,CAAC,CAACC,IAAI,CAAC;EAC/Cc,EAAE,EAAErC,GAAG,CAACyB,MAAM,CAAC,CAAC,CAACL,QAAQ,CAAC,CAAC;EAC3BlB,IAAI,EAAEF,GAAG,CAACyB,MAAM,CAAC,CAAC,CAACI,KAAK,CAAC,SAAS,CAAC,CAACT,QAAQ,CAAC,CAAC;EAC9CkB,UAAU,EAAEjB,uBAAuB;EACnCkB,QAAQ,EAAEX;AACZ,CAAC,CAAC;AAEF,MAAMY,gBAAgB,GAAGxC,GAAG,CAACI,KAAK,CAAY,CAAC,CAC5Cc,KAAK,CAACkB,aAAa,CAAC,CACpBK,MAAM,CAAC,IAAI,CAAC,CACZrB,QAAQ,CAAC,CAAC;AAEb,OAAO,SAASsB,mBAAmBA,CAACC,OAAuC,EAAE;EAC3E,IAAI,CAACA,OAAO,EAAE;IACZ,OAAOH,gBAAgB;EACzB;EAEA,MAAMI,qBAAsC,GAAGA,CAACnC,KAAK,EAAEC,OAAO,KAAK;IACjE,MAAMmC,cAAc,GAAGnD,SAAS,CAACoD,QAAQ,CAACC,IAAI,CAC3CC,OAAO,IAAKA,OAAO,CAACX,EAAE,KAAKM,OAC9B,CAAC;IAED,IAAI,CAACE,cAAc,EAAE;MACnB;MACA,OAAOpC,KAAK;IACd;IAEA,MAAMK,MAAM,GAAGtB,aAAa,CAACiB,KAAK,EAAwBoC,cAAc,CAAC;IAEzE,IAAI,CAAC/B,MAAM,EAAE;MACX,OAAOJ,OAAO,CAACM,KAAK,CAAC,YAAY,EAAE;QACjC2B,OAAO,EAAEhD,aAAa,CAACgD,OAAO;MAChC,CAAC,CAAC;IACJ;;IAEA;IACA,OAAOlC,KAAK;EACd,CAAC;EAED,OAAOT,GAAG,CAACI,KAAK,CAAY,CAAC,CAC1Bc,KAAK,CAACkB,aAAa,CAACa,MAAM,CAACL,qBAAqB,CAAC,CAAC,CAClDH,MAAM,CAAC,IAAI,CAAC,CACZrB,QAAQ,CAAC,CAAC;AACf;;AAEA;AACA;AACA","ignoreList":[]}
1
+ {"version":3,"file":"geospatial.js","names":["GeospatialFieldOptionsCountryEnum","Bourne","booleanWithin","JoiBase","countries","countriesDesc","England","NorthernIreland","Scotland","Wales","Joi","extend","type","base","array","messages","coerce","from","method","value","helpers","trimmed","trim","undefined","parse","result","errors","error","coordinatesSchema","items","number","required","featurePropertiesSchema","object","keys","description","string","coordinateGridReference","centroidGridReference","featureGeometrySchema","valid","coordinates","when","switch","is","then","min","featureSchema","id","properties","geometry","applySchemaConstraints","schema","def","options","constraints","isOptional","length","max","optional","getGeospatialSchema","country","at","unique","validateCountryBounds","countryFeature","features","find","feature","custom"],"sources":["../../../../../../src/server/plugins/engine/components/helpers/geospatial.ts"],"sourcesContent":["import {\n GeospatialFieldOptionsCountryEnum,\n type GeospatialFieldComponent,\n type GeospatialFieldOptionsCountry\n} from '@defra/forms-model'\nimport Bourne from '@hapi/bourne'\nimport { booleanWithin } from '@turf/boolean-within'\nimport JoiBase, { type CustomValidator } from 'joi'\n\nimport {\n type Coordinates,\n type Feature,\n type FeatureProperties,\n type Geometry\n} from '~/src/server/plugins/engine/types.js'\nimport { countries } from '~/src/server/plugins/map/routes/index.js'\n\nconst countriesDesc: Record<GeospatialFieldOptionsCountryEnum, string> = {\n [GeospatialFieldOptionsCountryEnum.England]: 'England',\n [GeospatialFieldOptionsCountryEnum.NorthernIreland]: 'Northern Ireland',\n [GeospatialFieldOptionsCountryEnum.Scotland]: 'Scotland',\n [GeospatialFieldOptionsCountryEnum.Wales]: 'Wales'\n}\n\nconst Joi = JoiBase.extend({\n type: 'array',\n base: JoiBase.array(),\n messages: {\n 'object.invalidjson': '{{#label}} must be a valid json array string'\n },\n coerce: {\n from: 'string',\n method(value, helpers) {\n if (typeof value === 'string') {\n const trimmed = value.trim()\n if (trimmed === '' || trimmed === '[]') {\n return {\n value: undefined\n }\n }\n\n try {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n return { value: Bourne.parse(value) }\n } catch {\n const result = {\n value,\n errors: [helpers.error('object.invalidjson')]\n }\n\n return result\n }\n } else {\n return {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n value\n }\n }\n }\n }\n}) as JoiBase.Root\n\nconst coordinatesSchema = Joi.array<Coordinates[]>()\n .items(Joi.number().required(), Joi.number().required())\n .required()\n\nconst featurePropertiesSchema = Joi.object<FeatureProperties>()\n .keys({\n description: Joi.string().required(),\n coordinateGridReference: Joi.string().required(),\n centroidGridReference: Joi.string().required()\n })\n .required()\n\nconst featureGeometrySchema = Joi.object<Geometry>().keys({\n type: Joi.string().valid('Point', 'LineString', 'Polygon').required(),\n coordinates: Joi.array()\n .when('type', {\n switch: [\n { is: 'Point', then: coordinatesSchema },\n {\n is: 'LineString',\n then: Joi.array().items(coordinatesSchema).min(2)\n },\n {\n is: 'Polygon',\n then: Joi.array().items(Joi.array().items(coordinatesSchema).min(3))\n }\n ]\n })\n .required()\n})\n\nconst featureSchema = Joi.object<Feature>().keys({\n id: Joi.string().required(),\n type: Joi.string().valid('Feature').required(),\n properties: featurePropertiesSchema,\n geometry: featureGeometrySchema\n})\n\nfunction applySchemaConstraints(\n schema: JoiBase.ArraySchema<Feature[]>,\n def: GeospatialFieldComponent\n) {\n const { options, schema: constraints } = def\n const isOptional = options.required === false\n\n if (typeof constraints?.length === 'number') {\n schema = schema.length(constraints.length)\n } else {\n if (typeof constraints?.min === 'number') {\n schema = schema.min(constraints.min)\n } else if (!isOptional) {\n schema = schema.min(1)\n }\n\n schema = schema.max(\n typeof constraints?.max === 'number' ? constraints.max : 50\n )\n }\n\n if (isOptional) {\n schema = schema.optional()\n } else {\n schema = schema.required()\n }\n\n return schema\n}\n\nexport function getGeospatialSchema(\n def: GeospatialFieldComponent\n): JoiBase.ArraySchema<Feature[]> {\n const { options = {} } = def\n const country: GeospatialFieldOptionsCountry | undefined =\n options.countries?.at(0)\n\n if (!country) {\n return applySchemaConstraints(\n Joi.array<Feature[]>().items(featureSchema).unique('id'),\n def\n )\n }\n\n const validateCountryBounds: CustomValidator = (value, helpers) => {\n const countryFeature = countries.features.find(\n (feature) => feature.id === country\n )\n\n if (!countryFeature) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return value\n }\n\n const result = booleanWithin(value as Geometry | Feature, countryFeature)\n\n if (!result) {\n return helpers.error('any.custom', {\n country: countriesDesc[country as GeospatialFieldOptionsCountryEnum]\n })\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return value\n }\n\n return applySchemaConstraints(\n Joi.array<Feature[]>()\n .items(featureSchema.custom(validateCountryBounds))\n .unique('id'),\n def\n )\n}\n\n/**\n * @import { CustomHelpers } from 'joi'\n */\n"],"mappings":"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","ignoreList":[]}
@@ -1,7 +1,17 @@
1
- import { GeospatialFieldOptionsCountryEnum } from '@defra/forms-model';
1
+ import { ComponentType, GeospatialFieldOptionsCountryEnum } from '@defra/forms-model';
2
2
  import { validState } from "./__stubs__/geospatial.js";
3
3
  import { getGeospatialSchema } from "./geospatial.js";
4
- const geospatialSchema = getGeospatialSchema();
4
+
5
+ /**
6
+ * @type {import('@defra/forms-model').GeospatialFieldComponent}
7
+ */
8
+ const geospatialComponent = {
9
+ name: 'geospatial',
10
+ title: 'Geospatial',
11
+ type: ComponentType.GeospatialField,
12
+ options: {}
13
+ };
14
+ const geospatialSchema = getGeospatialSchema(geospatialComponent);
5
15
  describe('Geospatial validation helpers', () => {
6
16
  test('it should not have errors for valid geojson object', () => {
7
17
  const result = geospatialSchema.validate(validState);
@@ -27,8 +37,19 @@ describe('Geospatial validation helpers', () => {
27
37
  });
28
38
  test('it should validate an empty array', () => {
29
39
  const result = geospatialSchema.validate('[]');
40
+ expect(result.error).toBeDefined();
41
+ expect(result.value).toBeUndefined();
42
+ });
43
+ test('it should validate an empty array when optional', () => {
44
+ const schema = getGeospatialSchema({
45
+ ...geospatialComponent,
46
+ options: {
47
+ required: false
48
+ }
49
+ });
50
+ const result = schema.validate('[]');
30
51
  expect(result.error).toBeUndefined();
31
- expect(result.value).toEqual([]);
52
+ expect(result.value).toBeUndefined();
32
53
  });
33
54
  test('it should not validate an empty object', () => {
34
55
  const result = geospatialSchema.validate('{}');
@@ -41,21 +62,31 @@ describe('Geospatial validation helpers', () => {
41
62
  expect(result.value).toBeUndefined();
42
63
  });
43
64
  test('it should be valid inside country bounds', () => {
44
- const schema = getGeospatialSchema(GeospatialFieldOptionsCountryEnum.England);
65
+ const schema = getGeospatialSchema({
66
+ ...geospatialComponent,
67
+ options: {
68
+ countries: [GeospatialFieldOptionsCountryEnum.England]
69
+ }
70
+ });
45
71
  expect(schema.validate(validState).error).toBeUndefined();
46
72
  expect(schema.validate(validState.slice(1)).error).toBeUndefined();
47
73
  expect(schema.validate(validState.slice(2)).error).toBeUndefined();
48
74
  expect(schema.validate(validState.slice(3)).error).toBeUndefined();
49
75
  });
50
76
  test('it should be invalid outside country bounds', () => {
51
- const schema = getGeospatialSchema(GeospatialFieldOptionsCountryEnum.Scotland);
77
+ const schema = getGeospatialSchema({
78
+ ...geospatialComponent,
79
+ options: {
80
+ countries: [GeospatialFieldOptionsCountryEnum.Scotland]
81
+ }
82
+ });
52
83
  expect(schema.validate(validState).error).toBeDefined();
53
84
  expect(schema.validate(validState.slice(1)).error).toBeDefined();
54
85
  expect(schema.validate(validState.slice(2)).error).toBeDefined();
55
86
  expect(schema.validate(validState.slice(3)).error).toBeDefined();
56
87
  });
57
88
  test('it should be valid with no country bounds', () => {
58
- const schema = getGeospatialSchema();
89
+ const schema = getGeospatialSchema(geospatialComponent);
59
90
  expect(schema.validate(validState).error).toBeUndefined();
60
91
  });
61
92
  });
@@ -1 +1 @@
1
- {"version":3,"file":"geospatial.test.js","names":["GeospatialFieldOptionsCountryEnum","validState","getGeospatialSchema","geospatialSchema","describe","test","result","validate","expect","error","toBeUndefined","value","toBeDefined","toHaveLength","JSON","stringify","toBe","toEqual","schema","England","slice","Scotland"],"sources":["../../../../../../src/server/plugins/engine/components/helpers/geospatial.test.js"],"sourcesContent":["import { GeospatialFieldOptionsCountryEnum } from '@defra/forms-model'\n\nimport { validState } from '~/src/server/plugins/engine/components/helpers/__stubs__/geospatial.js'\nimport { getGeospatialSchema } from '~/src/server/plugins/engine/components/helpers/geospatial.js'\n\nconst geospatialSchema = getGeospatialSchema()\n\ndescribe('Geospatial validation helpers', () => {\n test('it should not have errors for valid geojson object', () => {\n const result = geospatialSchema.validate(validState)\n\n expect(result.error).toBeUndefined()\n expect(result.value).toBeDefined()\n expect(result.value).toHaveLength(4)\n })\n\n test('it should not have errors for valid geojson string', () => {\n const result = geospatialSchema.validate(JSON.stringify(validState))\n\n expect(result.error).toBeUndefined()\n expect(result.value).toBeDefined()\n expect(result.value).toHaveLength(4)\n })\n\n test('it should have errors for invalid json string', () => {\n const result = geospatialSchema.validate('{')\n\n expect(result.error).toBeDefined()\n expect(result.value).toBe('{')\n })\n\n test('it should have errors for invalid geojson string', () => {\n const result = geospatialSchema.validate('[')\n\n expect(result.error).toBeDefined()\n expect(result.value).toBe('[')\n })\n\n test('it should validate an empty array', () => {\n const result = geospatialSchema.validate('[]')\n\n expect(result.error).toBeUndefined()\n expect(result.value).toEqual([])\n })\n\n test('it should not validate an empty object', () => {\n const result = geospatialSchema.validate('{}')\n\n expect(result.error).toBeDefined()\n expect(result.value).toBeUndefined()\n })\n\n test('it should validate an empty string', () => {\n const result = geospatialSchema.validate('')\n\n expect(result.error).toBeDefined()\n expect(result.value).toBeUndefined()\n })\n\n test('it should be valid inside country bounds', () => {\n const schema = getGeospatialSchema(\n GeospatialFieldOptionsCountryEnum.England\n )\n\n expect(schema.validate(validState).error).toBeUndefined()\n expect(schema.validate(validState.slice(1)).error).toBeUndefined()\n expect(schema.validate(validState.slice(2)).error).toBeUndefined()\n expect(schema.validate(validState.slice(3)).error).toBeUndefined()\n })\n\n test('it should be invalid outside country bounds', () => {\n const schema = getGeospatialSchema(\n GeospatialFieldOptionsCountryEnum.Scotland\n )\n\n expect(schema.validate(validState).error).toBeDefined()\n expect(schema.validate(validState.slice(1)).error).toBeDefined()\n expect(schema.validate(validState.slice(2)).error).toBeDefined()\n expect(schema.validate(validState.slice(3)).error).toBeDefined()\n })\n\n test('it should be valid with no country bounds', () => {\n const schema = getGeospatialSchema()\n\n expect(schema.validate(validState).error).toBeUndefined()\n })\n})\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"geospatial.test.js","names":["ComponentType","GeospatialFieldOptionsCountryEnum","validState","getGeospatialSchema","geospatialComponent","name","title","type","GeospatialField","options","geospatialSchema","describe","test","result","validate","expect","error","toBeUndefined","value","toBeDefined","toHaveLength","JSON","stringify","toBe","schema","required","countries","England","slice","Scotland"],"sources":["../../../../../../src/server/plugins/engine/components/helpers/geospatial.test.js"],"sourcesContent":["import {\n ComponentType,\n GeospatialFieldOptionsCountryEnum\n} from '@defra/forms-model'\n\nimport { validState } from '~/src/server/plugins/engine/components/helpers/__stubs__/geospatial.js'\nimport { getGeospatialSchema } from '~/src/server/plugins/engine/components/helpers/geospatial.js'\n\n/**\n * @type {import('@defra/forms-model').GeospatialFieldComponent}\n */\nconst geospatialComponent = {\n name: 'geospatial',\n title: 'Geospatial',\n type: ComponentType.GeospatialField,\n options: {}\n}\n\nconst geospatialSchema = getGeospatialSchema(geospatialComponent)\n\ndescribe('Geospatial validation helpers', () => {\n test('it should not have errors for valid geojson object', () => {\n const result = geospatialSchema.validate(validState)\n\n expect(result.error).toBeUndefined()\n expect(result.value).toBeDefined()\n expect(result.value).toHaveLength(4)\n })\n\n test('it should not have errors for valid geojson string', () => {\n const result = geospatialSchema.validate(JSON.stringify(validState))\n\n expect(result.error).toBeUndefined()\n expect(result.value).toBeDefined()\n expect(result.value).toHaveLength(4)\n })\n\n test('it should have errors for invalid json string', () => {\n const result = geospatialSchema.validate('{')\n\n expect(result.error).toBeDefined()\n expect(result.value).toBe('{')\n })\n\n test('it should have errors for invalid geojson string', () => {\n const result = geospatialSchema.validate('[')\n\n expect(result.error).toBeDefined()\n expect(result.value).toBe('[')\n })\n\n test('it should validate an empty array', () => {\n const result = geospatialSchema.validate('[]')\n\n expect(result.error).toBeDefined()\n expect(result.value).toBeUndefined()\n })\n\n test('it should validate an empty array when optional', () => {\n const schema = getGeospatialSchema({\n ...geospatialComponent,\n options: { required: false }\n })\n const result = schema.validate('[]')\n\n expect(result.error).toBeUndefined()\n expect(result.value).toBeUndefined()\n })\n\n test('it should not validate an empty object', () => {\n const result = geospatialSchema.validate('{}')\n\n expect(result.error).toBeDefined()\n expect(result.value).toBeUndefined()\n })\n\n test('it should validate an empty string', () => {\n const result = geospatialSchema.validate('')\n\n expect(result.error).toBeDefined()\n expect(result.value).toBeUndefined()\n })\n\n test('it should be valid inside country bounds', () => {\n const schema = getGeospatialSchema({\n ...geospatialComponent,\n options: { countries: [GeospatialFieldOptionsCountryEnum.England] }\n })\n\n expect(schema.validate(validState).error).toBeUndefined()\n expect(schema.validate(validState.slice(1)).error).toBeUndefined()\n expect(schema.validate(validState.slice(2)).error).toBeUndefined()\n expect(schema.validate(validState.slice(3)).error).toBeUndefined()\n })\n\n test('it should be invalid outside country bounds', () => {\n const schema = getGeospatialSchema({\n ...geospatialComponent,\n options: { countries: [GeospatialFieldOptionsCountryEnum.Scotland] }\n })\n\n expect(schema.validate(validState).error).toBeDefined()\n expect(schema.validate(validState.slice(1)).error).toBeDefined()\n expect(schema.validate(validState.slice(2)).error).toBeDefined()\n expect(schema.validate(validState.slice(3)).error).toBeDefined()\n })\n\n test('it should be valid with no country bounds', () => {\n const schema = getGeospatialSchema(geospatialComponent)\n\n expect(schema.validate(validState).error).toBeUndefined()\n })\n})\n"],"mappings":"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","ignoreList":[]}
@@ -37,7 +37,10 @@ export const messageTemplate = {
37
37
  dateMax: '{{#title}} must be the same as or before {{#limit}}',
38
38
  arrayMin: 'Select at least {{#limit}} options from the list',
39
39
  arrayMax: 'Only {{#limit}} can be selected from the list',
40
- arrayLength: 'Select only {{#limit}} options from the list'
40
+ arrayLength: 'Select only {{#limit}} options from the list',
41
+ featuresMin: 'Define at least {{#limit}} features',
42
+ featuresMax: 'Only {{#limit}} features can be defined',
43
+ featuresLength: 'Define exactly {{#limit}} features'
41
44
  };
42
45
  export const messages = {
43
46
  'string.base': messageTemplate.required,
@@ -1 +1 @@
1
- {"version":3,"file":"validationOptions.js","names":["joi","lowerFirstPreserveProperNouns","opts","functions","lowerFirst","messageTemplate","declarationRequired","expression","required","selectRequired","selectYesNoRequired","max","min","minMax","pattern","format","unicode","number","numberPrecision","numberInteger","numberMin","numberMax","maxWords","objectRequired","objectMissing","dateFormat","dateMin","dateMax","arrayMin","arrayMax","arrayLength","messages","messagesPre","validationOptions","abortEarly","errors","wrap","array","label"],"sources":["../../../../../src/server/plugins/engine/pageControllers/validationOptions.ts"],"sourcesContent":["// Declaration above is needed for: https://github.com/hapijs/joi/issues/3064\n\nimport joi, {\n type JoiExpression,\n type LanguageMessages,\n type LanguageMessagesExt,\n type ReferenceOptions,\n type ValidationOptions\n} from 'joi'\n\nimport { lowerFirstPreserveProperNouns } from '~/src/server/plugins/engine/components/helpers/index.js'\n\nconst opts = {\n functions: {\n lowerFirst: lowerFirstPreserveProperNouns\n }\n} as ReferenceOptions\n\n/**\n * see @link https://joi.dev/api/?v=17.4.2#template-syntax for template syntax\n */\nexport const messageTemplate: Record<string, JoiExpression> = {\n declarationRequired: joi.expression(\n 'You must confirm you understand and agree with the {{lowerFirst(#label)}} to continue',\n opts\n ) as JoiExpression,\n required: joi.expression(\n 'Enter {{lowerFirst(#label)}}',\n opts\n ) as JoiExpression,\n selectRequired: joi.expression(\n 'Select {{lowerFirst(#label)}}',\n opts\n ) as JoiExpression,\n selectYesNoRequired: '{{#label}} - select yes or no',\n max: '{{#label}} must be {{#limit}} characters or less',\n min: '{{#label}} must be {{#limit}} characters or more',\n minMax: '{{#label}} must be between {{#min}} and {{#max}} characters',\n pattern: joi.expression(\n 'Enter a valid {{lowerFirst(#label)}}',\n opts\n ) as JoiExpression,\n format: joi.expression(\n 'Enter {{lowerFirst(#label)}} in the correct format',\n opts\n ) as JoiExpression,\n unicode: '{{#label}} includes invalid characters, for example, long dashes',\n number: '{{#label}} must be a number',\n numberPrecision: '{{#label}} must have {{#limit}} or fewer decimal places',\n numberInteger: '{{#label}} must be a whole number',\n numberMin: '{{#label}} must be {{#limit}} or higher',\n numberMax: '{{#label}} must be {{#limit}} or lower',\n maxWords: '{{#label}} must be {{#limit}} words or fewer',\n\n // Nested fields use component title\n\n objectRequired: joi.expression('Enter {{#label}}', opts) as JoiExpression,\n objectMissing: joi.expression(\n '{{#title}} must include a {{lowerFirst(#label)}}',\n opts\n ) as JoiExpression,\n dateFormat: '{{#title}} must be a real date',\n dateMin: '{{#title}} must be the same as or after {{#limit}}',\n dateMax: '{{#title}} must be the same as or before {{#limit}}',\n arrayMin: 'Select at least {{#limit}} options from the list',\n arrayMax: 'Only {{#limit}} can be selected from the list',\n arrayLength: 'Select only {{#limit}} options from the list'\n}\n\nexport const messages: LanguageMessagesExt = {\n 'string.base': messageTemplate.required,\n 'string.min': messageTemplate.min,\n 'string.empty': messageTemplate.required,\n 'string.max': messageTemplate.max,\n 'string.email': messageTemplate.format,\n 'string.unicode': messageTemplate.unicode,\n 'string.pattern.base': messageTemplate.pattern,\n 'string.maxWords': messageTemplate.maxWords,\n\n 'number.base': messageTemplate.number,\n 'number.precision': messageTemplate.numberPrecision,\n 'number.integer': messageTemplate.numberInteger,\n 'number.unsafe': messageTemplate.format,\n 'number.min': messageTemplate.numberMin,\n 'number.max': messageTemplate.numberMax,\n\n 'object.required': messageTemplate.objectRequired,\n 'object.and': messageTemplate.objectMissing,\n\n 'any.only': messageTemplate.selectRequired,\n 'any.required': messageTemplate.selectRequired,\n 'any.empty': messageTemplate.required,\n\n 'date.base': messageTemplate.dateFormat,\n 'date.format': messageTemplate.dateFormat,\n 'date.min': messageTemplate.dateMin,\n 'date.max': messageTemplate.dateMax,\n\n 'object.invalidjson': messageTemplate.format\n}\n\nexport const messagesPre: LanguageMessages =\n messages as unknown as LanguageMessages\n\nexport const validationOptions: ValidationOptions = {\n abortEarly: false,\n messages: messagesPre,\n errors: {\n wrap: {\n array: false,\n label: false\n }\n }\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"validationOptions.js","names":["joi","lowerFirstPreserveProperNouns","opts","functions","lowerFirst","messageTemplate","declarationRequired","expression","required","selectRequired","selectYesNoRequired","max","min","minMax","pattern","format","unicode","number","numberPrecision","numberInteger","numberMin","numberMax","maxWords","objectRequired","objectMissing","dateFormat","dateMin","dateMax","arrayMin","arrayMax","arrayLength","featuresMin","featuresMax","featuresLength","messages","messagesPre","validationOptions","abortEarly","errors","wrap","array","label"],"sources":["../../../../../src/server/plugins/engine/pageControllers/validationOptions.ts"],"sourcesContent":["// Declaration above is needed for: https://github.com/hapijs/joi/issues/3064\n\nimport joi, {\n type JoiExpression,\n type LanguageMessages,\n type LanguageMessagesExt,\n type ReferenceOptions,\n type ValidationOptions\n} from 'joi'\n\nimport { lowerFirstPreserveProperNouns } from '~/src/server/plugins/engine/components/helpers/index.js'\n\nconst opts = {\n functions: {\n lowerFirst: lowerFirstPreserveProperNouns\n }\n} as ReferenceOptions\n\n/**\n * see @link https://joi.dev/api/?v=17.4.2#template-syntax for template syntax\n */\nexport const messageTemplate: Record<string, JoiExpression> = {\n declarationRequired: joi.expression(\n 'You must confirm you understand and agree with the {{lowerFirst(#label)}} to continue',\n opts\n ) as JoiExpression,\n required: joi.expression(\n 'Enter {{lowerFirst(#label)}}',\n opts\n ) as JoiExpression,\n selectRequired: joi.expression(\n 'Select {{lowerFirst(#label)}}',\n opts\n ) as JoiExpression,\n selectYesNoRequired: '{{#label}} - select yes or no',\n max: '{{#label}} must be {{#limit}} characters or less',\n min: '{{#label}} must be {{#limit}} characters or more',\n minMax: '{{#label}} must be between {{#min}} and {{#max}} characters',\n pattern: joi.expression(\n 'Enter a valid {{lowerFirst(#label)}}',\n opts\n ) as JoiExpression,\n format: joi.expression(\n 'Enter {{lowerFirst(#label)}} in the correct format',\n opts\n ) as JoiExpression,\n unicode: '{{#label}} includes invalid characters, for example, long dashes',\n number: '{{#label}} must be a number',\n numberPrecision: '{{#label}} must have {{#limit}} or fewer decimal places',\n numberInteger: '{{#label}} must be a whole number',\n numberMin: '{{#label}} must be {{#limit}} or higher',\n numberMax: '{{#label}} must be {{#limit}} or lower',\n maxWords: '{{#label}} must be {{#limit}} words or fewer',\n\n // Nested fields use component title\n\n objectRequired: joi.expression('Enter {{#label}}', opts) as JoiExpression,\n objectMissing: joi.expression(\n '{{#title}} must include a {{lowerFirst(#label)}}',\n opts\n ) as JoiExpression,\n dateFormat: '{{#title}} must be a real date',\n dateMin: '{{#title}} must be the same as or after {{#limit}}',\n dateMax: '{{#title}} must be the same as or before {{#limit}}',\n arrayMin: 'Select at least {{#limit}} options from the list',\n arrayMax: 'Only {{#limit}} can be selected from the list',\n arrayLength: 'Select only {{#limit}} options from the list',\n featuresMin: 'Define at least {{#limit}} features',\n featuresMax: 'Only {{#limit}} features can be defined',\n featuresLength: 'Define exactly {{#limit}} features'\n}\n\nexport const messages: LanguageMessagesExt = {\n 'string.base': messageTemplate.required,\n 'string.min': messageTemplate.min,\n 'string.empty': messageTemplate.required,\n 'string.max': messageTemplate.max,\n 'string.email': messageTemplate.format,\n 'string.unicode': messageTemplate.unicode,\n 'string.pattern.base': messageTemplate.pattern,\n 'string.maxWords': messageTemplate.maxWords,\n\n 'number.base': messageTemplate.number,\n 'number.precision': messageTemplate.numberPrecision,\n 'number.integer': messageTemplate.numberInteger,\n 'number.unsafe': messageTemplate.format,\n 'number.min': messageTemplate.numberMin,\n 'number.max': messageTemplate.numberMax,\n\n 'object.required': messageTemplate.objectRequired,\n 'object.and': messageTemplate.objectMissing,\n\n 'any.only': messageTemplate.selectRequired,\n 'any.required': messageTemplate.selectRequired,\n 'any.empty': messageTemplate.required,\n\n 'date.base': messageTemplate.dateFormat,\n 'date.format': messageTemplate.dateFormat,\n 'date.min': messageTemplate.dateMin,\n 'date.max': messageTemplate.dateMax,\n\n 'object.invalidjson': messageTemplate.format\n}\n\nexport const messagesPre: LanguageMessages =\n messages as unknown as LanguageMessages\n\nexport const validationOptions: ValidationOptions = {\n abortEarly: false,\n messages: messagesPre,\n errors: {\n wrap: {\n array: false,\n label: false\n }\n }\n}\n"],"mappings":"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","ignoreList":[]}
@@ -1,7 +1,7 @@
1
1
  {% from "govuk/components/textarea/macro.njk" import govukTextarea %}
2
2
 
3
3
  {% macro GeospatialField(component) %}
4
- <div class="app-geospatial-field" data-country="{{component.model.country}}">
4
+ <div class="app-geospatial-field" data-country="{{component.model.country}}" data-geometryTypes="{{component.model.geometryTypes}}">
5
5
  {{ govukTextarea(component.model) }}
6
6
  </div>
7
7
  {% endmacro %}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@defra/forms-engine-plugin",
3
- "version": "4.13.0",
3
+ "version": "4.14.1",
4
4
  "description": "Defra forms engine",
5
5
  "type": "module",
6
6
  "files": [
@@ -87,7 +87,7 @@
87
87
  },
88
88
  "license": "SEE LICENSE IN LICENSE",
89
89
  "dependencies": {
90
- "@defra/forms-model": "^3.0.663",
90
+ "@defra/forms-model": "^3.0.668",
91
91
  "@defra/hapi-tracing": "^1.29.0",
92
92
  "@defra/interactive-map": "^0.0.22-alpha",
93
93
  "@elastic/ecs-pino-format": "^1.5.0",
@@ -7,6 +7,7 @@ import {
7
7
  getCentroidGridRef,
8
8
  getCoordinateGridRef
9
9
  } from './map.js'
10
+ import { formatDelimtedList } from './utils.js'
10
11
 
11
12
  const helpPanelConfig = {
12
13
  showLabel: true,
@@ -28,8 +29,63 @@ const helpPanelConfig = {
28
29
  open: true,
29
30
  dismissible: true,
30
31
  modal: false
31
- },
32
- html: '<p class="govuk-body-s govuk-!-margin-bottom-2">You can add points, shapes or lines to the map.</p><ul class="govuk-list govuk-list--number govuk-body-s"><li>Search for a county, place or postcode</li><li>Use the + and - icons to zoom in and out</li><li>Double‑click, or select \'Done\', when you have finished drawing a line or shape</li><li>Give the location a name</li></ul>'
32
+ }
33
+ }
34
+
35
+ /**
36
+ * @param {boolean} allowLine
37
+ * @param {boolean} allowShape
38
+ */
39
+ function getLineOrShapeText(allowLine, allowShape) {
40
+ if (allowLine && allowShape) {
41
+ return 'a line or shape'
42
+ }
43
+ if (allowLine) {
44
+ return 'a line'
45
+ }
46
+ if (allowShape) {
47
+ return 'a shape'
48
+ }
49
+ return ''
50
+ }
51
+
52
+ /**
53
+ * @param {boolean} allowPoint
54
+ * @param {boolean} allowLine
55
+ * @param {boolean} allowShape
56
+ */
57
+ function getAllowedTypesPhrase(allowPoint, allowLine, allowShape) {
58
+ const items = []
59
+
60
+ if (allowPoint) {
61
+ items.push('points')
62
+ }
63
+ if (allowLine) {
64
+ items.push('lines')
65
+ }
66
+ if (allowShape) {
67
+ items.push('shapes')
68
+ }
69
+
70
+ return formatDelimtedList(items, ',', 'or')
71
+ }
72
+
73
+ /**
74
+ * @param {boolean} allowPoint
75
+ * @param {boolean} allowLine
76
+ * @param {boolean} allowShape
77
+ */
78
+ export function getHelpPanelHtml(allowPoint, allowLine, allowShape) {
79
+ const lineOrShapeText = getLineOrShapeText(allowLine, allowShape)
80
+ const doneExtra = lineOrShapeText
81
+ ? `<li>Double‑click, or select 'Done', when you have finished drawing ${lineOrShapeText}</li>`
82
+ : ''
83
+ const allowedTypesText = getAllowedTypesPhrase(
84
+ allowPoint,
85
+ allowLine,
86
+ allowShape
87
+ )
88
+ return `<p class="govuk-body-s govuk-!-margin-bottom-2">You can add ${allowedTypesText} to the map.</p><ul class="govuk-list govuk-list--number govuk-body-s"><li>Search for a county, place or postcode</li><li>Use the + and - icons to zoom in and out</li>${doneExtra}<li>Give the location a name</li></ul>`
33
89
  }
34
90
 
35
91
  const lineFeatureProperties = {
@@ -157,7 +213,18 @@ export function processGeospatial(config, geospatial, index) {
157
213
  const { map, interactPlugin } = createMap(mapId, initConfig, config)
158
214
  const featuresManager = getFeaturesManager(geojson)
159
215
  const activeFeatureManager = getActiveFeatureManager()
160
- const uiManager = getUIManager(geojson, map, mapId, listEl, geospatialInput)
216
+ const geometryTypes = geospatial.dataset.geometrytypes
217
+ const options = {
218
+ geometryTypes
219
+ }
220
+ const uiManager = getUIManager(
221
+ geojson,
222
+ map,
223
+ mapId,
224
+ listEl,
225
+ geospatialInput,
226
+ options
227
+ )
161
228
 
162
229
  /**
163
230
  * @type {Context}
@@ -492,16 +559,41 @@ function getValueRenderer(geojson, geospatialInput) {
492
559
  * @param {string} mapId - the ID of the map
493
560
  * @param {HTMLDivElement} listEl - where to render the feature list
494
561
  * @param {HTMLTextAreaElement} geospatialInput - the geospatial textarea
562
+ * @param { UIManagerOptions | undefined } options - extra options such as allowable geometry types
495
563
  */
496
- function getUIManager(geojson, map, mapId, listEl, geospatialInput) {
564
+ export function getUIManager(
565
+ geojson,
566
+ map,
567
+ mapId,
568
+ listEl,
569
+ geospatialInput,
570
+ options
571
+ ) {
572
+ /**
573
+ * Get a CSV list of geometry types the user can create
574
+ * @returns {string[]}
575
+ */
576
+ function getAllowableGeometryTypes() {
577
+ return options?.geometryTypes
578
+ ? options.geometryTypes.split(',')
579
+ : ['point', 'line', 'shape']
580
+ }
581
+
497
582
  /**
498
583
  * Toggle the hidden state of the action buttons
499
584
  * @type {ToggleActionButtons}
500
585
  */
501
586
  function toggleActionButtons(hidden) {
502
- map.toggleButtonState('btnAddPoint', 'hidden', hidden)
503
- map.toggleButtonState('btnAddPolygon', 'hidden', hidden)
504
- map.toggleButtonState('btnAddLine', 'hidden', hidden)
587
+ const types = getAllowableGeometryTypes()
588
+ if (types.includes('point')) {
589
+ map.toggleButtonState('btnAddPoint', 'hidden', hidden)
590
+ }
591
+ if (types.includes('shape')) {
592
+ map.toggleButtonState('btnAddPolygon', 'hidden', hidden)
593
+ }
594
+ if (types.includes('line')) {
595
+ map.toggleButtonState('btnAddLine', 'hidden', hidden)
596
+ }
505
597
  }
506
598
 
507
599
  /**
@@ -528,7 +620,8 @@ function getUIManager(geojson, map, mapId, listEl, geospatialInput) {
528
620
  renderValue,
529
621
  listEl,
530
622
  toggleActionButtons,
531
- focusDescriptionInput
623
+ focusDescriptionInput,
624
+ getAllowableGeometryTypes
532
625
  }
533
626
  }
534
627
 
@@ -572,7 +665,8 @@ function createContainers(geospatialInput, index) {
572
665
  function onMapReadyFactory(context) {
573
666
  const { map, activeFeatureManager, uiManager, interactPlugin, drawPlugin } =
574
667
  context
575
- const { toggleActionButtons, renderList } = uiManager
668
+ const { toggleActionButtons, renderList, getAllowableGeometryTypes } =
669
+ uiManager
576
670
  const { resetActiveFeature } = activeFeatureManager
577
671
 
578
672
  /**
@@ -581,53 +675,67 @@ function onMapReadyFactory(context) {
581
675
  * @param {MapLibreMap} e.map - the map provider instance
582
676
  */
583
677
  return function onMapReady(e) {
678
+ const types = getAllowableGeometryTypes()
679
+ const allowPoint = types.includes('point')
680
+ const allowLine = types.includes('line')
681
+ const allowShape = types.includes('shape')
682
+
584
683
  // Add info panel
585
- map.addPanel('info', helpPanelConfig)
586
-
587
- map.addButton('btnAddPoint', {
588
- variant: 'tertiary',
589
- label: 'Add point',
590
- iconSvgContent: POINT_SVG,
591
- onClick: () => {
592
- resetActiveFeature()
593
- toggleActionButtons(true)
594
- renderList(true)
595
- interactPlugin.enable()
596
- },
597
- mobile: { slot: 'actions' },
598
- tablet: { slot: 'actions' },
599
- desktop: { slot: 'actions' }
684
+ map.addPanel('info', {
685
+ ...helpPanelConfig,
686
+ html: getHelpPanelHtml(allowPoint, allowLine, allowShape)
600
687
  })
601
688
 
602
- map.addButton('btnAddPolygon', {
603
- variant: 'tertiary',
604
- label: 'Add shape',
605
- iconSvgContent: POLYGON_SVG,
606
- onClick: () => {
607
- resetActiveFeature()
608
- toggleActionButtons(true)
609
- renderList(true)
610
- drawPlugin.newPolygon(generateID(), polygonFeatureProperties)
611
- },
612
- mobile: { slot: 'actions' },
613
- tablet: { slot: 'actions' },
614
- desktop: { slot: 'actions' }
615
- })
689
+ if (allowPoint) {
690
+ map.addButton('btnAddPoint', {
691
+ variant: 'tertiary',
692
+ label: 'Add point',
693
+ iconSvgContent: POINT_SVG,
694
+ onClick: () => {
695
+ resetActiveFeature()
696
+ toggleActionButtons(true)
697
+ renderList(true)
698
+ interactPlugin.enable()
699
+ },
700
+ mobile: { slot: 'actions' },
701
+ tablet: { slot: 'actions' },
702
+ desktop: { slot: 'actions' }
703
+ })
704
+ }
616
705
 
617
- map.addButton('btnAddLine', {
618
- variant: 'tertiary',
619
- label: 'Add line',
620
- iconSvgContent: LINE_SVG,
621
- onClick: () => {
622
- resetActiveFeature()
623
- toggleActionButtons(true)
624
- renderList(true)
625
- drawPlugin.newLine(generateID(), lineFeatureProperties)
626
- },
627
- mobile: { slot: 'actions' },
628
- tablet: { slot: 'actions' },
629
- desktop: { slot: 'actions' }
630
- })
706
+ if (allowShape) {
707
+ map.addButton('btnAddPolygon', {
708
+ variant: 'tertiary',
709
+ label: 'Add shape',
710
+ iconSvgContent: POLYGON_SVG,
711
+ onClick: () => {
712
+ resetActiveFeature()
713
+ toggleActionButtons(true)
714
+ renderList(true)
715
+ drawPlugin.newPolygon(generateID(), polygonFeatureProperties)
716
+ },
717
+ mobile: { slot: 'actions' },
718
+ tablet: { slot: 'actions' },
719
+ desktop: { slot: 'actions' }
720
+ })
721
+ }
722
+
723
+ if (allowLine) {
724
+ map.addButton('btnAddLine', {
725
+ variant: 'tertiary',
726
+ label: 'Add line',
727
+ iconSvgContent: LINE_SVG,
728
+ onClick: () => {
729
+ resetActiveFeature()
730
+ toggleActionButtons(true)
731
+ renderList(true)
732
+ drawPlugin.newLine(generateID(), lineFeatureProperties)
733
+ },
734
+ mobile: { slot: 'actions' },
735
+ tablet: { slot: 'actions' },
736
+ desktop: { slot: 'actions' }
737
+ })
738
+ }
631
739
 
632
740
  // Set the map provider on the context
633
741
  context.mapProvider = e.map
@@ -1055,6 +1163,12 @@ function onListElKeydownFactory() {
1055
1163
  * @returns {void}
1056
1164
  */
1057
1165
 
1166
+ /**
1167
+ * Returns the list of geometry types a user can create
1168
+ * @callback GetAllowableGeometryTypes
1169
+ * @returns {string[]}
1170
+ */
1171
+
1058
1172
  /**
1059
1173
  * Set focus to the last description input
1060
1174
  * @callback FocusDescriptionInput
@@ -1084,6 +1198,7 @@ function onListElKeydownFactory() {
1084
1198
  * @property {HTMLDivElement} listEl - the summary list of features
1085
1199
  * @property {ToggleActionButtons} toggleActionButtons - function that toggles the action buttons
1086
1200
  * @property {FocusDescriptionInput} focusDescriptionInput - function that sets focus to a description input element
1201
+ * @property {GetAllowableGeometryTypes} getAllowableGeometryTypes - function that returns the array of geometry types a user can create
1087
1202
  */
1088
1203
 
1089
1204
  /**
@@ -1098,5 +1213,5 @@ function onListElKeydownFactory() {
1098
1213
  */
1099
1214
 
1100
1215
  /**
1101
- * @import { MapLibreMap } from './map.js'
1216
+ * @import { MapLibreMap, UIManagerOptions } from './map.js'
1102
1217
  */
@@ -395,6 +395,11 @@ export function centerMap(map, mapProvider, center) {
395
395
  * @property {TileData} data - the tile data config
396
396
  */
397
397
 
398
+ /**
399
+ * @typedef {object} UIManagerOptions
400
+ * @property {string} [geometryTypes] - the CSV list of geometry types that a user can create
401
+ */
402
+
398
403
  /**
399
404
  * @import { Feature } from '../../server/plugins/engine/types.js'
400
405
  */
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Builds a text representation of a list in the form 'a, b, c, d or e'
3
+ * @param {string[]} items
4
+ * @param {string} separator
5
+ * @param {string} lastSpearator
6
+ */
7
+ export function formatDelimtedList(items, separator, lastSpearator) {
8
+ if (items.length === 0) {
9
+ return ''
10
+ }
11
+
12
+ if (items.length === 1) {
13
+ return items[0]
14
+ }
15
+
16
+ if (items.length === 2) {
17
+ return `${items[0]} ${lastSpearator} ${items[1]}`
18
+ }
19
+
20
+ const last = items.pop()
21
+ const separatorAndSpace = `${separator} `
22
+ return `${items.join(separatorAndSpace)} ${lastSpearator} ${last}`
23
+ }