@defra/forms-engine-plugin 4.0.41 → 4.0.42

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 (90) hide show
  1. package/.public/javascripts/application.min.js +1 -1
  2. package/.public/javascripts/application.min.js.map +1 -1
  3. package/.public/javascripts/shared.min.js +1 -1
  4. package/.public/javascripts/shared.min.js.map +1 -1
  5. package/.public/stylesheets/application.min.css +2 -2
  6. package/.public/stylesheets/application.min.css.map +1 -1
  7. package/.server/client/javascripts/location-map.d.ts +93 -0
  8. package/.server/client/javascripts/location-map.js +745 -0
  9. package/.server/client/javascripts/location-map.js.map +1 -0
  10. package/.server/client/javascripts/shared.d.ts +4 -0
  11. package/.server/client/javascripts/shared.js +5 -0
  12. package/.server/client/javascripts/shared.js.map +1 -1
  13. package/.server/client/stylesheets/_location-fields.scss +11 -0
  14. package/.server/client/stylesheets/application.scss +0 -1
  15. package/.server/client/stylesheets/shared.scss +1 -0
  16. package/.server/config/index.js +1 -1
  17. package/.server/config/index.js.map +1 -1
  18. package/.server/server/plugins/engine/plugin.js +11 -0
  19. package/.server/server/plugins/engine/plugin.js.map +1 -1
  20. package/.server/server/plugins/engine/views/components/_location-field-base.html +1 -1
  21. package/.server/server/plugins/engine/views/components/osgridreffield.html +4 -2
  22. package/.server/server/plugins/map/index.d.ts +7 -0
  23. package/.server/server/plugins/map/index.js +20 -0
  24. package/.server/server/plugins/map/index.js.map +1 -0
  25. package/.server/server/plugins/map/routes/index.d.ts +20 -0
  26. package/.server/server/plugins/map/routes/index.js +128 -0
  27. package/.server/server/plugins/map/routes/index.js.map +1 -0
  28. package/.server/server/plugins/map/routes/vts/OS_VTS_3857/resources/sprites/dark.json +690 -0
  29. package/.server/server/plugins/map/routes/vts/OS_VTS_3857/resources/sprites/dark.png +0 -0
  30. package/.server/server/plugins/map/routes/vts/OS_VTS_3857/resources/sprites/dark@2x.json +690 -0
  31. package/.server/server/plugins/map/routes/vts/OS_VTS_3857/resources/sprites/dark@2x.png +0 -0
  32. package/.server/server/plugins/map/routes/vts/OS_VTS_3857/resources/sprites/greyscale.json +690 -0
  33. package/.server/server/plugins/map/routes/vts/OS_VTS_3857/resources/sprites/greyscale.png +0 -0
  34. package/.server/server/plugins/map/routes/vts/OS_VTS_3857/resources/sprites/greyscale@2x.json +690 -0
  35. package/.server/server/plugins/map/routes/vts/OS_VTS_3857/resources/sprites/greyscale@2x.png +0 -0
  36. package/.server/server/plugins/map/routes/vts/OS_VTS_3857/resources/sprites/sprite.json +690 -0
  37. package/.server/server/plugins/map/routes/vts/OS_VTS_3857/resources/sprites/sprite.png +0 -0
  38. package/.server/server/plugins/map/routes/vts/OS_VTS_3857/resources/sprites/sprite@2x.json +690 -0
  39. package/.server/server/plugins/map/routes/vts/OS_VTS_3857/resources/sprites/sprite@2x.png +0 -0
  40. package/.server/server/plugins/map/routes/vts/OS_VTS_3857_Black_and_White.json +7858 -0
  41. package/.server/server/plugins/map/routes/vts/OS_VTS_3857_Dark.json +7669 -0
  42. package/.server/server/plugins/map/routes/vts/OS_VTS_3857_Outdoor.json +7653 -0
  43. package/.server/server/plugins/map/routes/vts/README.md +5 -0
  44. package/.server/server/plugins/map/service.d.ts +14 -0
  45. package/.server/server/plugins/map/service.js +76 -0
  46. package/.server/server/plugins/map/service.js.map +1 -0
  47. package/.server/server/plugins/map/service.test.js +120 -0
  48. package/.server/server/plugins/map/service.test.js.map +1 -0
  49. package/.server/server/plugins/map/test/__stubs__/find.d.ts +3 -0
  50. package/.server/server/plugins/map/test/__stubs__/find.js +216 -0
  51. package/.server/server/plugins/map/test/__stubs__/find.js.map +1 -0
  52. package/.server/server/plugins/map/test/__stubs__/nearest.d.ts +37 -0
  53. package/.server/server/plugins/map/test/__stubs__/nearest.js +38 -0
  54. package/.server/server/plugins/map/test/__stubs__/nearest.js.map +1 -0
  55. package/.server/server/plugins/map/types.d.ts +232 -0
  56. package/.server/server/plugins/map/types.js +120 -0
  57. package/.server/server/plugins/map/types.js.map +1 -0
  58. package/package.json +3 -1
  59. package/src/client/javascripts/location-map.js +766 -0
  60. package/src/client/javascripts/shared.js +4 -0
  61. package/src/client/stylesheets/_location-fields.scss +11 -0
  62. package/src/client/stylesheets/application.scss +0 -1
  63. package/src/client/stylesheets/shared.scss +1 -0
  64. package/src/config/index.ts +1 -1
  65. package/src/server/plugins/engine/plugin.ts +11 -0
  66. package/src/server/plugins/engine/views/components/_location-field-base.html +1 -1
  67. package/src/server/plugins/engine/views/components/osgridreffield.html +4 -2
  68. package/src/server/plugins/map/index.js +19 -0
  69. package/src/server/plugins/map/routes/index.js +146 -0
  70. package/src/server/plugins/map/routes/vts/OS_VTS_3857/resources/sprites/dark.json +690 -0
  71. package/src/server/plugins/map/routes/vts/OS_VTS_3857/resources/sprites/dark.png +0 -0
  72. package/src/server/plugins/map/routes/vts/OS_VTS_3857/resources/sprites/dark@2x.json +690 -0
  73. package/src/server/plugins/map/routes/vts/OS_VTS_3857/resources/sprites/dark@2x.png +0 -0
  74. package/src/server/plugins/map/routes/vts/OS_VTS_3857/resources/sprites/greyscale.json +690 -0
  75. package/src/server/plugins/map/routes/vts/OS_VTS_3857/resources/sprites/greyscale.png +0 -0
  76. package/src/server/plugins/map/routes/vts/OS_VTS_3857/resources/sprites/greyscale@2x.json +690 -0
  77. package/src/server/plugins/map/routes/vts/OS_VTS_3857/resources/sprites/greyscale@2x.png +0 -0
  78. package/src/server/plugins/map/routes/vts/OS_VTS_3857/resources/sprites/sprite.json +690 -0
  79. package/src/server/plugins/map/routes/vts/OS_VTS_3857/resources/sprites/sprite.png +0 -0
  80. package/src/server/plugins/map/routes/vts/OS_VTS_3857/resources/sprites/sprite@2x.json +690 -0
  81. package/src/server/plugins/map/routes/vts/OS_VTS_3857/resources/sprites/sprite@2x.png +0 -0
  82. package/src/server/plugins/map/routes/vts/OS_VTS_3857_Black_and_White.json +7858 -0
  83. package/src/server/plugins/map/routes/vts/OS_VTS_3857_Dark.json +7669 -0
  84. package/src/server/plugins/map/routes/vts/OS_VTS_3857_Outdoor.json +7653 -0
  85. package/src/server/plugins/map/routes/vts/README.md +5 -0
  86. package/src/server/plugins/map/service.js +84 -0
  87. package/src/server/plugins/map/service.test.js +144 -0
  88. package/src/server/plugins/map/test/__stubs__/find.js +271 -0
  89. package/src/server/plugins/map/test/__stubs__/nearest.js +46 -0
  90. package/src/server/plugins/map/types.js +119 -0
@@ -2,12 +2,16 @@ import { initAllAutocomplete as initAllAutocompleteImp } from '~/src/client/java
2
2
  import { initFileUpload as initFileUploadImp } from '~/src/client/javascripts/file-upload.js'
3
3
  import { initAllGovuk as initAllGovukImp } from '~/src/client/javascripts/govuk.js'
4
4
  import { initPreviewCloseLink as initPreviewCloseLinkImp } from '~/src/client/javascripts/preview-close-link.js'
5
+ export { initMaps } from '~/src/client/javascripts/location-map.js'
5
6
 
6
7
  export const initAllGovuk = initAllGovukImp
7
8
  export const initAllAutocomplete = initAllAutocompleteImp
8
9
  export const initFileUpload = initFileUploadImp
9
10
  export const initPreviewCloseLink = initPreviewCloseLinkImp
10
11
 
12
+ /**
13
+ * Initialise all clientside components (but not maps as this will be an opt-in for now given the additional UMD assets that are required)
14
+ */
11
15
  export function initAll() {
12
16
  initAllGovuk()
13
17
  initAllAutocomplete()
@@ -3,3 +3,14 @@
3
3
  .govuk-form-group .app-location-field-inputs .govuk-form-group {
4
4
  margin-bottom: 0;
5
5
  }
6
+
7
+ .map-container {
8
+ margin-top: 10px;
9
+ }
10
+
11
+ // Hides the location instructions text when JS (maps) is available
12
+ .js-enabled .app-location-field {
13
+ details:last-child {
14
+ display: none;
15
+ }
16
+ }
@@ -2,7 +2,6 @@
2
2
  @use "shared";
3
3
  @use "code";
4
4
  @use "tag-env";
5
- @use "location-fields";
6
5
 
7
6
  // An example of some user-supplied styling
8
7
  // Not great practice but it illustrates the point
@@ -2,6 +2,7 @@
2
2
  @use "pkg:accessible-autocomplete";
3
3
  @use "prose";
4
4
  @use "summary-list";
5
+ @use "location-fields";
5
6
 
6
7
  // Use default GDS Transport font for autocomplete
7
8
  .autocomplete__hint,
@@ -255,7 +255,7 @@ export const config = convict({
255
255
  } as SchemaObj<string>,
256
256
 
257
257
  ordnanceSurveyApiKey: {
258
- doc: 'The ordnance survey api key use by the postcode lookup plugin',
258
+ doc: 'The ordnance survey api key used by the postcode lookup and maps plugin',
259
259
  format: String,
260
260
  nullable: true,
261
261
  default: undefined,
@@ -15,6 +15,7 @@ import { getRoutes as getRepeaterItemDeleteRoutes } from '~/src/server/plugins/e
15
15
  import { getRoutes as getRepeaterSummaryRoutes } from '~/src/server/plugins/engine/routes/repeaters/summary.js'
16
16
  import { type PluginOptions } from '~/src/server/plugins/engine/types.js'
17
17
  import { registerVision } from '~/src/server/plugins/engine/vision.js'
18
+ import { mapPlugin } from '~/src/server/plugins/map/index.js'
18
19
  import { postcodeLookupPlugin } from '~/src/server/plugins/postcode-lookup/index.js'
19
20
  import {
20
21
  type FormRequestPayloadRefs,
@@ -57,6 +58,16 @@ export const plugin = {
57
58
  })
58
59
  }
59
60
 
61
+ // Register the maps plugin only if we have an OS api key
62
+ if (ordnanceSurveyApiKey) {
63
+ await server.register({
64
+ plugin: mapPlugin,
65
+ options: {
66
+ ordnanceSurveyApiKey
67
+ }
68
+ })
69
+ }
70
+
60
71
  server.expose('baseLayoutPath', nunjucksOptions.baseLayoutPath)
61
72
  server.expose('viewContext', viewContext)
62
73
  server.expose('cacheService', cacheService)
@@ -70,7 +70,7 @@
70
70
 
71
71
  {% set hasErrors = showFieldsetError and component.model.errorMessage %}
72
72
 
73
- <div class="govuk-form-group {{ "govuk-form-group--error" if hasErrors }}">
73
+ <div class="govuk-form-group app-location-field {{ "govuk-form-group--error" if hasErrors }}" data-locationtype="{{component.type | lower}}">
74
74
  {{ govukFieldset({
75
75
  legend: {
76
76
  text: component.model.fieldset.legend.text,
@@ -3,8 +3,10 @@
3
3
 
4
4
  {% macro OsGridRefField(component) %}
5
5
  {% set hasErrors = component.model.errorMessage %}
6
- <div class="govuk-form-group {{ "govuk-form-group--error" if hasErrors }}">
7
- {{ TextField(component) }}
6
+ <div class="govuk-form-group app-location-field {{ "govuk-form-group--error" if hasErrors }}" data-locationtype="{{component.type | lower}}">
7
+ <div class="app-location-field-inputs">
8
+ {{ TextField(component) }}
9
+ </div>
8
10
 
9
11
  {% if component.model.instructionText %}
10
12
  {{ govukDetails({
@@ -0,0 +1,19 @@
1
+ import { getRoutes } from '~/src/server/plugins/map/routes/index.js'
2
+
3
+ /**
4
+ * @satisfies {NamedPlugin<MapConfiguration>}
5
+ */
6
+ export const mapPlugin = {
7
+ name: '@defra/forms-engine-plugin/map',
8
+ dependencies: ['@hapi/inert'],
9
+ multiple: false,
10
+ register(server, options) {
11
+ // @ts-expect-error - Request typing
12
+ server.route(getRoutes(options))
13
+ }
14
+ }
15
+
16
+ /**
17
+ * @import { NamedPlugin } from '@hapi/hapi'
18
+ * @import { MapConfiguration } from '~/src/server/plugins/map/types.js'
19
+ */
@@ -0,0 +1,146 @@
1
+ import { resolve } from 'node:path'
2
+
3
+ import Joi from 'joi'
4
+
5
+ import { find, nearest } from '~/src/server/plugins/map/service.js'
6
+ import { request as httpRequest } from '~/src/server/services/httpService.js'
7
+
8
+ /**
9
+ * Gets the map support routes
10
+ * @param {MapConfiguration} options - ordnance survey names api key
11
+ */
12
+ export function getRoutes(options) {
13
+ return [
14
+ mapProxyRoute(options),
15
+ geocodeProxyRoute(options),
16
+ reverseGeocodeProxyRoute(options),
17
+ ...tileRoutes()
18
+ ]
19
+ }
20
+
21
+ /**
22
+ * Proxies ordnance survey requests from the front end to api.os.com
23
+ * Used for VTS map tiles, sprites and fonts by forwarding on the request
24
+ * and adding the apikey and optionally an SRS (spatial reference system)
25
+ * @param {MapConfiguration} options - the map options
26
+ * @returns {ServerRoute<MapProxyGetRequestRefs>}
27
+ */
28
+ function mapProxyRoute(options) {
29
+ return {
30
+ method: 'GET',
31
+ path: '/api/map-proxy',
32
+ handler: async (request, h) => {
33
+ const { query } = request
34
+ const targetUrl = new URL(decodeURIComponent(query.url))
35
+
36
+ // Add API key server-side and set SRS
37
+ targetUrl.searchParams.set('key', options.ordnanceSurveyApiKey)
38
+ if (!targetUrl.searchParams.has('srs')) {
39
+ targetUrl.searchParams.set('srs', '3857')
40
+ }
41
+
42
+ const proxyResponse = await httpRequest('get', targetUrl.toString())
43
+ const buffer = proxyResponse.payload
44
+ const contentType = proxyResponse.res.headers['content-type']
45
+ const response = h.response(buffer)
46
+
47
+ if (contentType) {
48
+ response.type(contentType)
49
+ }
50
+
51
+ return response
52
+ },
53
+ options: {
54
+ validate: {
55
+ query: Joi.object()
56
+ .keys({
57
+ url: Joi.string().required()
58
+ })
59
+ .optional()
60
+ }
61
+ }
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Proxies ordnance survey geocode requests from the front end to api.os.com
67
+ * Used for the gazzeteer address lookup to find name from query strings like postcode and place names
68
+ * @param {MapConfiguration} options - the map options
69
+ * @returns {ServerRoute<MapGeocodeGetRequestRefs>}
70
+ */
71
+ function geocodeProxyRoute(options) {
72
+ return {
73
+ method: 'GET',
74
+ path: '/api/geocode-proxy',
75
+ async handler(request, _h) {
76
+ const { query } = request
77
+ const data = await find(query.query, options.ordnanceSurveyApiKey)
78
+
79
+ return data
80
+ },
81
+ options: {
82
+ validate: {
83
+ query: Joi.object()
84
+ .keys({
85
+ query: Joi.string().required()
86
+ })
87
+ .required()
88
+ }
89
+ }
90
+ }
91
+ }
92
+
93
+ /**
94
+ * Proxies ordnance survey reverse geocode requests from the front end to api.os.com
95
+ * Used to find name from easting and northing points.
96
+ * N.B this endpoint is currently not used by the front end but will be soon in "maps V2"
97
+ * @param {MapConfiguration} options - the map options
98
+ * @returns {ServerRoute<MapReverseGeocodeGetRequestRefs>}
99
+ */
100
+ function reverseGeocodeProxyRoute(options) {
101
+ return {
102
+ method: 'GET',
103
+ path: '/api/reverse-geocode-proxy',
104
+ async handler(request, _h) {
105
+ const { query } = request
106
+ const data = await nearest(
107
+ query.easting,
108
+ query.northing,
109
+ options.ordnanceSurveyApiKey
110
+ )
111
+
112
+ return data
113
+ },
114
+ options: {
115
+ validate: {
116
+ query: Joi.object()
117
+ .keys({
118
+ easting: Joi.number().required(),
119
+ northing: Joi.number().required()
120
+ })
121
+ .required()
122
+ }
123
+ }
124
+ }
125
+ }
126
+
127
+ function tileRoutes() {
128
+ return [
129
+ {
130
+ method: 'GET',
131
+ path: '/api/maps/vts/{path*}',
132
+ options: {
133
+ handler: {
134
+ directory: {
135
+ path: resolve(import.meta.dirname, './vts')
136
+ }
137
+ }
138
+ }
139
+ }
140
+ ]
141
+ }
142
+
143
+ /**
144
+ * @import { ServerRoute } from '@hapi/hapi'
145
+ * @import { MapConfiguration, MapProxyGetRequestRefs, MapGeocodeGetRequestRefs, MapReverseGeocodeGetRequestRefs } from '~/src/server/plugins/map/types.js'
146
+ */