@defra/forms-engine-plugin 3.0.9 → 4.0.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 (127) hide show
  1. package/.public/stylesheets/application.min.css +3 -3
  2. package/.public/stylesheets/application.min.css.map +1 -1
  3. package/.server/client/stylesheets/application.scss +14 -0
  4. package/.server/config/index.d.ts +1 -0
  5. package/.server/config/index.js +7 -0
  6. package/.server/config/index.js.map +1 -1
  7. package/.server/index.js +6 -2
  8. package/.server/index.js.map +1 -1
  9. package/.server/server/constants.d.ts +2 -0
  10. package/.server/server/constants.js +2 -0
  11. package/.server/server/constants.js.map +1 -1
  12. package/.server/server/forms/components.json +7 -0
  13. package/.server/server/forms/register-as-a-unicorn-breeder.yaml +18 -2
  14. package/.server/server/plugins/engine/components/UkAddressField.d.ts +15 -9
  15. package/.server/server/plugins/engine/components/UkAddressField.js +67 -6
  16. package/.server/server/plugins/engine/components/UkAddressField.js.map +1 -1
  17. package/.server/server/plugins/engine/configureEnginePlugin.d.ts +1 -1
  18. package/.server/server/plugins/engine/configureEnginePlugin.js +6 -3
  19. package/.server/server/plugins/engine/configureEnginePlugin.js.map +1 -1
  20. package/.server/server/plugins/engine/models/FormModel.d.ts +2 -0
  21. package/.server/server/plugins/engine/models/FormModel.js +3 -1
  22. package/.server/server/plugins/engine/models/FormModel.js.map +1 -1
  23. package/.server/server/plugins/engine/options.js +2 -1
  24. package/.server/server/plugins/engine/options.js.map +1 -1
  25. package/.server/server/plugins/engine/pageControllers/QuestionPageController.d.ts +1 -0
  26. package/.server/server/plugins/engine/pageControllers/QuestionPageController.js +46 -3
  27. package/.server/server/plugins/engine/pageControllers/QuestionPageController.js.map +1 -1
  28. package/.server/server/plugins/engine/plugin.js +15 -2
  29. package/.server/server/plugins/engine/plugin.js.map +1 -1
  30. package/.server/server/plugins/engine/routes/index.d.ts +2 -2
  31. package/.server/server/plugins/engine/routes/index.js +49 -9
  32. package/.server/server/plugins/engine/routes/index.js.map +1 -1
  33. package/.server/server/plugins/engine/routes/questions.d.ts +4 -4
  34. package/.server/server/plugins/engine/routes/questions.js +10 -10
  35. package/.server/server/plugins/engine/routes/questions.js.map +1 -1
  36. package/.server/server/plugins/engine/routes/repeaters/item-delete.d.ts +2 -1
  37. package/.server/server/plugins/engine/routes/repeaters/item-delete.js +31 -27
  38. package/.server/server/plugins/engine/routes/repeaters/item-delete.js.map +1 -1
  39. package/.server/server/plugins/engine/routes/repeaters/summary.d.ts +2 -1
  40. package/.server/server/plugins/engine/routes/repeaters/summary.js +31 -27
  41. package/.server/server/plugins/engine/routes/repeaters/summary.js.map +1 -1
  42. package/.server/server/plugins/engine/types.d.ts +21 -3
  43. package/.server/server/plugins/engine/types.js.map +1 -1
  44. package/.server/server/plugins/engine/validationHelpers.d.ts +15 -0
  45. package/.server/server/plugins/engine/validationHelpers.js +29 -0
  46. package/.server/server/plugins/engine/validationHelpers.js.map +1 -0
  47. package/.server/server/plugins/engine/views/components/ukaddressfield.html +50 -6
  48. package/.server/server/plugins/engine/vision.js +3 -1
  49. package/.server/server/plugins/engine/vision.js.map +1 -1
  50. package/.server/server/plugins/postcode-lookup/index.d.ts +8 -0
  51. package/.server/server/plugins/postcode-lookup/index.js +21 -0
  52. package/.server/server/plugins/postcode-lookup/index.js.map +1 -0
  53. package/.server/server/plugins/postcode-lookup/models/index.d.ts +255 -0
  54. package/.server/server/plugins/postcode-lookup/models/index.js +517 -0
  55. package/.server/server/plugins/postcode-lookup/models/index.js.map +1 -0
  56. package/.server/server/plugins/postcode-lookup/routes/index.d.ts +19 -0
  57. package/.server/server/plugins/postcode-lookup/routes/index.js +267 -0
  58. package/.server/server/plugins/postcode-lookup/routes/index.js.map +1 -0
  59. package/.server/server/plugins/postcode-lookup/service.d.ts +26 -0
  60. package/.server/server/plugins/postcode-lookup/service.js +148 -0
  61. package/.server/server/plugins/postcode-lookup/service.js.map +1 -0
  62. package/.server/server/plugins/postcode-lookup/service.test.js +144 -0
  63. package/.server/server/plugins/postcode-lookup/service.test.js.map +1 -0
  64. package/.server/server/plugins/postcode-lookup/test/__stubs__/postcode.d.ts +282 -0
  65. package/.server/server/plugins/postcode-lookup/test/__stubs__/postcode.js +370 -0
  66. package/.server/server/plugins/postcode-lookup/test/__stubs__/postcode.js.map +1 -0
  67. package/.server/server/plugins/postcode-lookup/test/__stubs__/query.d.ts +131 -0
  68. package/.server/server/plugins/postcode-lookup/test/__stubs__/query.js +195 -0
  69. package/.server/server/plugins/postcode-lookup/test/__stubs__/query.js.map +1 -0
  70. package/.server/server/plugins/postcode-lookup/test/__stubs__/uprn.d.ts +51 -0
  71. package/.server/server/plugins/postcode-lookup/test/__stubs__/uprn.js +52 -0
  72. package/.server/server/plugins/postcode-lookup/test/__stubs__/uprn.js.map +1 -0
  73. package/.server/server/plugins/postcode-lookup/types.d.ts +204 -0
  74. package/.server/server/plugins/postcode-lookup/types.js +144 -0
  75. package/.server/server/plugins/postcode-lookup/types.js.map +1 -0
  76. package/.server/server/plugins/postcode-lookup/views/postcode-lookup-details.html +83 -0
  77. package/.server/server/routes/types.d.ts +7 -2
  78. package/.server/server/routes/types.js +6 -0
  79. package/.server/server/routes/types.js.map +1 -1
  80. package/.server/server/schemas/index.js +1 -1
  81. package/.server/server/schemas/index.js.map +1 -1
  82. package/.server/server/types.d.ts +1 -0
  83. package/.server/server/types.js.map +1 -1
  84. package/package.json +2 -2
  85. package/src/client/stylesheets/application.scss +14 -0
  86. package/src/config/index.ts +9 -1
  87. package/src/index.ts +5 -4
  88. package/src/server/constants.js +2 -0
  89. package/src/server/forms/components.json +7 -0
  90. package/src/server/forms/register-as-a-unicorn-breeder.yaml +18 -2
  91. package/src/server/plugins/engine/components/UkAddressField.test.ts +50 -27
  92. package/src/server/plugins/engine/components/UkAddressField.ts +91 -8
  93. package/src/server/plugins/engine/configureEnginePlugin.ts +5 -3
  94. package/src/server/plugins/engine/helpers.test.ts +2 -1
  95. package/src/server/plugins/engine/models/FormModel.ts +10 -2
  96. package/src/server/plugins/engine/options.js +2 -1
  97. package/src/server/plugins/engine/pageControllers/PageController.test.ts +2 -1
  98. package/src/server/plugins/engine/pageControllers/QuestionPageController.test.ts +9 -4
  99. package/src/server/plugins/engine/pageControllers/QuestionPageController.ts +69 -1
  100. package/src/server/plugins/engine/pageControllers/SummaryPageController.test.ts +2 -1
  101. package/src/server/plugins/engine/plugin.ts +22 -4
  102. package/src/server/plugins/engine/routes/index.test.ts +317 -0
  103. package/src/server/plugins/engine/routes/index.ts +81 -8
  104. package/src/server/plugins/engine/routes/questions.test.ts +126 -15
  105. package/src/server/plugins/engine/routes/questions.ts +71 -57
  106. package/src/server/plugins/engine/routes/repeaters/item-delete.test.ts +83 -0
  107. package/src/server/plugins/engine/routes/repeaters/item-delete.ts +39 -33
  108. package/src/server/plugins/engine/routes/repeaters/summary.test.ts +75 -0
  109. package/src/server/plugins/engine/routes/repeaters/summary.ts +28 -22
  110. package/src/server/plugins/engine/types.ts +27 -8
  111. package/src/server/plugins/engine/validationHelpers.ts +48 -0
  112. package/src/server/plugins/engine/views/components/ukaddressfield.html +50 -6
  113. package/src/server/plugins/engine/vision.ts +6 -0
  114. package/src/server/plugins/postcode-lookup/index.js +21 -0
  115. package/src/server/plugins/postcode-lookup/models/index.js +549 -0
  116. package/src/server/plugins/postcode-lookup/routes/index.js +258 -0
  117. package/src/server/plugins/postcode-lookup/service.js +188 -0
  118. package/src/server/plugins/postcode-lookup/service.test.js +177 -0
  119. package/src/server/plugins/postcode-lookup/test/__stubs__/postcode.js +382 -0
  120. package/src/server/plugins/postcode-lookup/test/__stubs__/query.js +200 -0
  121. package/src/server/plugins/postcode-lookup/test/__stubs__/uprn.js +53 -0
  122. package/src/server/plugins/postcode-lookup/types.js +143 -0
  123. package/src/server/plugins/postcode-lookup/views/postcode-lookup-details.html +83 -0
  124. package/src/server/postcode-lookup.test.ts +64 -0
  125. package/src/server/routes/types.ts +11 -2
  126. package/src/server/schemas/index.ts +5 -7
  127. package/src/server/types.ts +1 -0
@@ -0,0 +1,75 @@
1
+ import { getRoutes } from '~/src/server/plugins/engine/routes/repeaters/summary.js'
2
+ import { type OnRequestCallback } from '~/src/server/plugins/engine/types.js'
3
+
4
+ describe('repeater summary routes', () => {
5
+ describe('route configuration', () => {
6
+ it('should return all expected routes', () => {
7
+ const routes = getRoutes({}, {}, undefined)
8
+
9
+ expect(routes).toHaveLength(4)
10
+
11
+ const expectedPaths = [
12
+ '/{slug}/{path}/summary',
13
+ '/preview/{state}/{slug}/{path}/summary'
14
+ ]
15
+
16
+ expectedPaths.forEach((path) => {
17
+ const getRoute = routes.find(
18
+ (route) => route.method === 'get' && route.path === path
19
+ )
20
+ const postRoute = routes.find(
21
+ (route) => route.method === 'post' && route.path === path
22
+ )
23
+
24
+ expect(getRoute).toBeDefined()
25
+ expect(postRoute).toBeDefined()
26
+ })
27
+ })
28
+
29
+ it('should pass onRequest callback to handlers', () => {
30
+ const onRequestCallback: OnRequestCallback = jest
31
+ .fn()
32
+ .mockResolvedValue(undefined)
33
+ const routes = getRoutes({}, {}, onRequestCallback)
34
+
35
+ // Test that the handlers are created with the onRequest callback
36
+ const getRoute = routes.find(
37
+ (route) =>
38
+ route.method === 'get' && route.path === '/{slug}/{path}/summary'
39
+ )
40
+ const postRoute = routes.find(
41
+ (route) =>
42
+ route.method === 'post' && route.path === '/{slug}/{path}/summary'
43
+ )
44
+
45
+ expect(getRoute?.handler).toBeDefined()
46
+ expect(postRoute?.handler).toBeDefined()
47
+ })
48
+ })
49
+
50
+ describe('handler functionality', () => {
51
+ it('should create handlers that accept onRequest callback', () => {
52
+ const onRequestCallback: OnRequestCallback = jest
53
+ .fn()
54
+ .mockResolvedValue(undefined)
55
+ const routes = getRoutes({}, {}, onRequestCallback)
56
+
57
+ // Test that the handlers are created with the onRequest callback
58
+ const getRoute = routes.find(
59
+ (route) =>
60
+ route.method === 'get' && route.path === '/{slug}/{path}/summary'
61
+ )
62
+ const postRoute = routes.find(
63
+ (route) =>
64
+ route.method === 'post' && route.path === '/{slug}/{path}/summary'
65
+ )
66
+
67
+ expect(getRoute?.handler).toBeDefined()
68
+ expect(postRoute?.handler).toBeDefined()
69
+
70
+ // Test that handlers are functions
71
+ expect(typeof getRoute?.handler).toBe('function')
72
+ expect(typeof postRoute?.handler).toBe('function')
73
+ })
74
+ })
75
+ })
@@ -6,6 +6,7 @@ import Joi from 'joi'
6
6
 
7
7
  import { RepeatPageController } from '~/src/server/plugins/engine/pageControllers/RepeatPageController.js'
8
8
  import { redirectOrMakeHandler } from '~/src/server/plugins/engine/routes/index.js'
9
+ import { type OnRequestCallback } from '~/src/server/plugins/engine/types.js'
9
10
  import {
10
11
  type FormRequest,
11
12
  type FormRequestPayload,
@@ -20,41 +21,46 @@ import {
20
21
  stateSchema
21
22
  } from '~/src/server/schemas/index.js'
22
23
 
23
- function getHandler(request: FormRequest, h: FormResponseToolkit) {
24
- const { params } = request
24
+ function getHandler(onRequest?: OnRequestCallback) {
25
+ return async function (request: FormRequest, h: FormResponseToolkit) {
26
+ const { params } = request
25
27
 
26
- return redirectOrMakeHandler(request, h, (page, context) => {
27
- if (!(page instanceof RepeatPageController)) {
28
- throw Boom.notFound(`No repeater page found for /${params.path}`)
29
- }
28
+ return redirectOrMakeHandler(request, h, onRequest, (page, context) => {
29
+ if (!(page instanceof RepeatPageController)) {
30
+ throw Boom.notFound(`No repeater page found for /${params.path}`)
31
+ }
30
32
 
31
- return page.makeGetListSummaryRouteHandler()(request, context, h)
32
- })
33
+ return page.makeGetListSummaryRouteHandler()(request, context, h)
34
+ })
35
+ }
33
36
  }
34
37
 
35
- function postHandler(request: FormRequestPayload, h: FormResponseToolkit) {
36
- const { params } = request
38
+ function postHandler(onRequest?: OnRequestCallback) {
39
+ return async function (request: FormRequestPayload, h: FormResponseToolkit) {
40
+ const { params } = request
37
41
 
38
- return redirectOrMakeHandler(request, h, (page, context) => {
39
- const { isForceAccess } = context
42
+ return redirectOrMakeHandler(request, h, onRequest, (page, context) => {
43
+ const { isForceAccess } = context
40
44
 
41
- if (isForceAccess || !(page instanceof RepeatPageController)) {
42
- throw Boom.notFound(`No repeater page found for /${params.path}`)
43
- }
45
+ if (isForceAccess || !(page instanceof RepeatPageController)) {
46
+ throw Boom.notFound(`No repeater page found for /${params.path}`)
47
+ }
44
48
 
45
- return page.makePostListSummaryRouteHandler()(request, context, h)
46
- })
49
+ return page.makePostListSummaryRouteHandler()(request, context, h)
50
+ })
51
+ }
47
52
  }
48
53
 
49
54
  export function getRoutes(
50
55
  getRouteOptions: RouteOptions<FormRequestRefs>,
51
- postRouteOptions: RouteOptions<FormRequestPayloadRefs>
56
+ postRouteOptions: RouteOptions<FormRequestPayloadRefs>,
57
+ onRequest?: OnRequestCallback
52
58
  ): (ServerRoute<FormRequestRefs> | ServerRoute<FormRequestPayloadRefs>)[] {
53
59
  return [
54
60
  {
55
61
  method: 'get',
56
62
  path: '/{slug}/{path}/summary',
57
- handler: getHandler,
63
+ handler: getHandler(onRequest),
58
64
  options: {
59
65
  ...getRouteOptions,
60
66
  validate: {
@@ -69,7 +75,7 @@ export function getRoutes(
69
75
  {
70
76
  method: 'get',
71
77
  path: '/preview/{state}/{slug}/{path}/summary',
72
- handler: getHandler,
78
+ handler: getHandler(onRequest),
73
79
  options: {
74
80
  ...getRouteOptions,
75
81
  validate: {
@@ -85,7 +91,7 @@ export function getRoutes(
85
91
  {
86
92
  method: 'post',
87
93
  path: '/{slug}/{path}/summary',
88
- handler: postHandler,
94
+ handler: postHandler(onRequest),
89
95
  options: {
90
96
  ...postRouteOptions,
91
97
  validate: {
@@ -106,7 +112,7 @@ export function getRoutes(
106
112
  {
107
113
  method: 'post',
108
114
  path: '/preview/{state}/{slug}/{path}/summary',
109
- handler: postHandler,
115
+ handler: postHandler(onRequest),
110
116
  options: {
111
117
  ...postRouteOptions,
112
118
  validate: {
@@ -1,12 +1,11 @@
1
1
  import {
2
2
  type ComponentDef,
3
3
  type Event,
4
- type FormDefinition,
5
- type FormMetadata,
6
4
  type FormVersionMetadata,
7
5
  type Item,
8
6
  type List,
9
- type Page
7
+ type Page,
8
+ type UkAddressFieldComponent
10
9
  } from '@defra/forms-model'
11
10
  import {
12
11
  type PluginProperties,
@@ -30,6 +29,7 @@ import { type FormModel } from '~/src/server/plugins/engine/models/index.js'
30
29
  import { type DetailItemField } from '~/src/server/plugins/engine/models/types.js'
31
30
  import { type PageController } from '~/src/server/plugins/engine/pageControllers/PageController.js'
32
31
  import { type PageControllerClass } from '~/src/server/plugins/engine/pageControllers/helpers/pages.js'
32
+ import { type QuestionPageController } from '~/src/server/plugins/engine/pageControllers/index.js'
33
33
  import {
34
34
  type FileStatus,
35
35
  type FormAdapterSubmissionSchemaVersion,
@@ -38,7 +38,6 @@ import {
38
38
  import { type ViewContext } from '~/src/server/plugins/nunjucks/types.js'
39
39
  import {
40
40
  type FormAction,
41
- type FormParams,
42
41
  type FormRequest,
43
42
  type FormRequestPayload,
44
43
  type FormResponseToolkit,
@@ -368,10 +367,12 @@ export type PreparePageEventRequestOptions = (
368
367
 
369
368
  export type OnRequestCallback = (
370
369
  request: AnyFormRequest,
371
- params: FormParams,
372
- definition: FormDefinition,
373
- metadata: FormMetadata
374
- ) => void
370
+ h: FormResponseToolkit,
371
+ context: FormContext
372
+ ) =>
373
+ | ResponseObject
374
+ | FormResponseToolkit['continue']
375
+ | Promise<ResponseObject | FormResponseToolkit['continue']>
375
376
 
376
377
  export type SaveAndExitHandler = (
377
378
  request: FormRequestPayload,
@@ -379,6 +380,23 @@ export type SaveAndExitHandler = (
379
380
  context: FormContext
380
381
  ) => ResponseObject
381
382
 
383
+ export interface ExternalArgs {
384
+ component: ComponentDef
385
+ controller: QuestionPageController
386
+ sourceUrl: string
387
+ actionArgs: Record<string, string>
388
+ }
389
+
390
+ export interface PostcodeLookupExternalArgs extends ExternalArgs {
391
+ component: UkAddressFieldComponent
392
+ actionArgs: { step: string }
393
+ }
394
+
395
+ export interface ExternalStateAppendage {
396
+ component: string
397
+ data: FormStateValue | FormState
398
+ }
399
+
382
400
  export interface PluginOptions {
383
401
  model?: FormModel
384
402
  services?: Services
@@ -396,6 +414,7 @@ export interface PluginOptions {
396
414
  preparePageEventRequestOptions?: PreparePageEventRequestOptions
397
415
  onRequest?: OnRequestCallback
398
416
  baseUrl: string // base URL of the application, protocol and hostname e.g. "https://myapp.com"
417
+ ordnanceSurveyApiKey?: string
399
418
  }
400
419
 
401
420
  export interface FormAdapterSubmissionMessageMeta {
@@ -0,0 +1,48 @@
1
+ import { type ResponseObject } from '@hapi/hapi'
2
+
3
+ import * as Components from '~/src/server/plugins/engine/components/index.js'
4
+ import {
5
+ type FormRequestPayload,
6
+ type FormResponseToolkit
7
+ } from '~/src/server/plugins/engine/types/index.js'
8
+ import { type ExternalArgs } from '~/src/server/plugins/engine/types.js'
9
+
10
+ // Type guard for ExternalComponent
11
+ export function isExternalComponent(
12
+ component: unknown
13
+ ): component is ExternalComponent {
14
+ return typeof (component as ExternalComponent).dispatcher === 'function'
15
+ }
16
+
17
+ // External components are guaranteed to have a dispatcher method
18
+ export interface ExternalComponent {
19
+ dispatcher(
20
+ request: FormRequestPayload,
21
+ h: FormResponseToolkit,
22
+ args: ExternalArgs
23
+ ): ResponseObject
24
+ }
25
+
26
+ /**
27
+ * Returns internal and external components from a componentMap, regardless of error state.
28
+ * @returns An object containing internalComponents and externalComponents arrays
29
+ */
30
+ export function getComponentsByType(): {
31
+ internalComponents: Map<string, unknown>
32
+ externalComponents: Map<string, ExternalComponent>
33
+ } {
34
+ const internalComponents = new Map<string, unknown>()
35
+ const externalComponents = new Map<string, ExternalComponent>()
36
+
37
+ const componentMap = new Map<string, unknown>(Object.entries(Components))
38
+
39
+ for (const [name, component] of componentMap.entries()) {
40
+ if (isExternalComponent(component)) {
41
+ externalComponents.set(name, component)
42
+ } else {
43
+ internalComponents.set(name, component)
44
+ }
45
+ }
46
+
47
+ return { internalComponents, externalComponents }
48
+ }
@@ -1,10 +1,18 @@
1
1
  {% from "partials/components.html" import componentList %}
2
2
  {% from "govuk/components/fieldset/macro.njk" import govukFieldset %}
3
3
  {% from "govuk/components/hint/macro.njk" import govukHint %}
4
+ {% from "govuk/components/button/macro.njk" import govukButton %}
5
+ {% from "govuk/components/inset-text/macro.njk" import govukInsetText %}
4
6
 
5
7
  {% macro UkAddressField(component) %}
6
8
  {% set fieldset = component.model.fieldset %}
7
- {% set addressFieldHtml = componentList(component.model.components) %}
9
+ {% set usePostcodeLookup = component.model.usePostcodeLookup %}
10
+
11
+ {% set addressFieldHtml %}
12
+ <div {{"hidden" if usePostcodeLookup }}>
13
+ {{ componentList(component.model.components) }}
14
+ </div>
15
+ {% endset %}
8
16
 
9
17
  {% if component.model.hint %}
10
18
  {% set addressHintHtml %}
@@ -17,9 +25,45 @@
17
25
  {% set addressFieldHtml = addressHintHtml + addressFieldHtml %}
18
26
  {% endif %}
19
27
 
20
- {{ govukFieldset({
21
- legend: fieldset.legend,
22
- attributes: fieldset.attributes,
23
- html: addressFieldHtml
24
- }) if fieldset else addressFieldHtml }}
28
+ <div id="{{component.model.name}}" class="govuk-form-group{{ ' govuk-form-group--error' if usePostcodeLookup and component.model.errors | length }}">
29
+ {{ govukFieldset({
30
+ legend: fieldset.legend,
31
+ attributes: fieldset.attributes,
32
+ html: addressFieldHtml
33
+ }) if fieldset else addressFieldHtml }}
34
+
35
+ {% if usePostcodeLookup %}
36
+ {% set value = component.model.value %}
37
+
38
+ {% if value %}
39
+ {% set insetHtml %}
40
+ <strong>Selected address:</strong>
41
+ <br><br>
42
+ {{ value }}
43
+ <br><br>
44
+ <p class="govuk-body">
45
+ <button class="govuk-link govuk-button--link govuk-!-margin-right-1" name="action"
46
+ value="external-{{component.model.name}}">Use a different address</button>
47
+ </p>
48
+ {% endset %}
49
+
50
+ {{ govukInsetText({
51
+ html: insetHtml,
52
+ classes: "govuk-!-margin-top-2"
53
+ }) }}
54
+ {% else %}
55
+ <div class="govuk-button-group govuk-!-margin-bottom-0">
56
+ {{ govukButton({
57
+ text: "Find an address",
58
+ attributes: {
59
+ name: "action",
60
+ value: "external-" + component.model.name
61
+ },
62
+ classes: "govuk-button--secondary govuk-!-margin-right-1 govuk-!-margin-bottom-0"
63
+ }) }}
64
+ <p class="govuk-body govuk-!-margin-bottom-0">or <button class="govuk-link govuk-button--link govuk-!-margin-right-1 govuk-!-margin-bottom-0" name="action" value="external-{{component.model.name}}--step:manual">enter address manually</button></p>
65
+ </div>
66
+ {% endif %}
67
+ {% endif %}
68
+ </div>
25
69
  {% endmacro %}
@@ -13,6 +13,7 @@ import {
13
13
  prepareNunjucksEnvironment
14
14
  } from '~/src/server/plugins/engine/index.js'
15
15
  import { type PluginOptions } from '~/src/server/plugins/engine/types.js'
16
+ import { VIEW_PATH as POSTCODE_LOOKUP_VIEW_PATH } from '~/src/server/plugins/postcode-lookup/index.js'
16
17
 
17
18
  export async function registerVision(
18
19
  server: Server,
@@ -24,10 +25,15 @@ export async function registerVision(
24
25
  )
25
26
 
26
27
  const viewPathResolved = join(packageRoot, VIEW_PATH)
28
+ const postcodeLookupPathResolved = join(
29
+ packageRoot,
30
+ POSTCODE_LOOKUP_VIEW_PATH
31
+ )
27
32
 
28
33
  const paths = [
29
34
  ...pluginOptions.nunjucks.paths,
30
35
  viewPathResolved,
36
+ postcodeLookupPathResolved,
31
37
  join(govukFrontendPath, 'dist')
32
38
  ]
33
39
 
@@ -0,0 +1,21 @@
1
+ import { getRoutes } from '~/src/server/plugins/postcode-lookup/routes/index.js'
2
+
3
+ export const VIEW_PATH = 'src/server/plugins/postcode-lookup/views'
4
+
5
+ /**
6
+ * @satisfies {NamedPlugin<PostcodeLookupConfiguration>}
7
+ */
8
+ export const postcodeLookupPlugin = {
9
+ name: '@defra/forms-engine-plugin/postcode-lookup',
10
+ dependencies: ['@hapi/vision'],
11
+ multiple: false,
12
+ register(server, options) {
13
+ // @ts-expect-error - Request typing
14
+ server.route(getRoutes(options))
15
+ }
16
+ }
17
+
18
+ /**
19
+ * @import { NamedPlugin } from '@hapi/hapi'
20
+ * @import { PostcodeLookupConfiguration } from '~/src/server/plugins/postcode-lookup/types.js'
21
+ */