@defra/forms-engine-plugin 4.0.0 → 4.0.2
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/stylesheets/application.min.css +2 -2
- package/.public/stylesheets/application.min.css.map +1 -1
- package/.server/client/stylesheets/shared.scss +15 -0
- package/.server/config/index.d.ts +1 -0
- package/.server/config/index.js +7 -0
- package/.server/config/index.js.map +1 -1
- package/.server/index.js +6 -2
- package/.server/index.js.map +1 -1
- package/.server/server/constants.d.ts +2 -0
- package/.server/server/constants.js +2 -0
- package/.server/server/constants.js.map +1 -1
- package/.server/server/forms/components.json +7 -0
- package/.server/server/forms/register-as-a-unicorn-breeder.yaml +18 -2
- package/.server/server/plugins/engine/components/UkAddressField.d.ts +15 -9
- package/.server/server/plugins/engine/components/UkAddressField.js +67 -6
- package/.server/server/plugins/engine/components/UkAddressField.js.map +1 -1
- package/.server/server/plugins/engine/configureEnginePlugin.d.ts +1 -1
- package/.server/server/plugins/engine/configureEnginePlugin.js +6 -3
- package/.server/server/plugins/engine/configureEnginePlugin.js.map +1 -1
- package/.server/server/plugins/engine/models/FormModel.d.ts +2 -0
- package/.server/server/plugins/engine/models/FormModel.js +3 -1
- package/.server/server/plugins/engine/models/FormModel.js.map +1 -1
- package/.server/server/plugins/engine/options.js +2 -1
- package/.server/server/plugins/engine/options.js.map +1 -1
- package/.server/server/plugins/engine/pageControllers/QuestionPageController.d.ts +1 -0
- package/.server/server/plugins/engine/pageControllers/QuestionPageController.js +46 -3
- package/.server/server/plugins/engine/pageControllers/QuestionPageController.js.map +1 -1
- package/.server/server/plugins/engine/plugin.js +13 -1
- package/.server/server/plugins/engine/plugin.js.map +1 -1
- package/.server/server/plugins/engine/routes/index.js +41 -3
- package/.server/server/plugins/engine/routes/index.js.map +1 -1
- package/.server/server/plugins/engine/types.d.ts +19 -1
- package/.server/server/plugins/engine/types.js.map +1 -1
- package/.server/server/plugins/engine/validationHelpers.d.ts +15 -0
- package/.server/server/plugins/engine/validationHelpers.js +29 -0
- package/.server/server/plugins/engine/validationHelpers.js.map +1 -0
- package/.server/server/plugins/engine/views/components/ukaddressfield.html +50 -6
- package/.server/server/plugins/engine/vision.js +3 -1
- package/.server/server/plugins/engine/vision.js.map +1 -1
- package/.server/server/plugins/postcode-lookup/index.d.ts +8 -0
- package/.server/server/plugins/postcode-lookup/index.js +21 -0
- package/.server/server/plugins/postcode-lookup/index.js.map +1 -0
- package/.server/server/plugins/postcode-lookup/models/index.d.ts +255 -0
- package/.server/server/plugins/postcode-lookup/models/index.js +517 -0
- package/.server/server/plugins/postcode-lookup/models/index.js.map +1 -0
- package/.server/server/plugins/postcode-lookup/routes/index.d.ts +19 -0
- package/.server/server/plugins/postcode-lookup/routes/index.js +267 -0
- package/.server/server/plugins/postcode-lookup/routes/index.js.map +1 -0
- package/.server/server/plugins/postcode-lookup/service.d.ts +26 -0
- package/.server/server/plugins/postcode-lookup/service.js +148 -0
- package/.server/server/plugins/postcode-lookup/service.js.map +1 -0
- package/.server/server/plugins/postcode-lookup/service.test.js +144 -0
- package/.server/server/plugins/postcode-lookup/service.test.js.map +1 -0
- package/.server/server/plugins/postcode-lookup/test/__stubs__/postcode.d.ts +282 -0
- package/.server/server/plugins/postcode-lookup/test/__stubs__/postcode.js +370 -0
- package/.server/server/plugins/postcode-lookup/test/__stubs__/postcode.js.map +1 -0
- package/.server/server/plugins/postcode-lookup/test/__stubs__/query.d.ts +131 -0
- package/.server/server/plugins/postcode-lookup/test/__stubs__/query.js +195 -0
- package/.server/server/plugins/postcode-lookup/test/__stubs__/query.js.map +1 -0
- package/.server/server/plugins/postcode-lookup/test/__stubs__/uprn.d.ts +51 -0
- package/.server/server/plugins/postcode-lookup/test/__stubs__/uprn.js +52 -0
- package/.server/server/plugins/postcode-lookup/test/__stubs__/uprn.js.map +1 -0
- package/.server/server/plugins/postcode-lookup/types.d.ts +204 -0
- package/.server/server/plugins/postcode-lookup/types.js +144 -0
- package/.server/server/plugins/postcode-lookup/types.js.map +1 -0
- package/.server/server/plugins/postcode-lookup/views/postcode-lookup-details.html +83 -0
- package/.server/server/routes/types.d.ts +6 -1
- package/.server/server/routes/types.js +6 -0
- package/.server/server/routes/types.js.map +1 -1
- package/.server/server/schemas/index.js +1 -1
- package/.server/server/schemas/index.js.map +1 -1
- package/.server/server/types.d.ts +1 -0
- package/.server/server/types.js.map +1 -1
- package/package.json +2 -2
- package/src/client/stylesheets/shared.scss +15 -0
- package/src/config/index.ts +9 -1
- package/src/index.ts +5 -4
- package/src/server/constants.js +2 -0
- package/src/server/forms/components.json +7 -0
- package/src/server/forms/register-as-a-unicorn-breeder.yaml +18 -2
- package/src/server/plugins/engine/components/UkAddressField.test.ts +50 -27
- package/src/server/plugins/engine/components/UkAddressField.ts +91 -8
- package/src/server/plugins/engine/configureEnginePlugin.ts +5 -3
- package/src/server/plugins/engine/models/FormModel.ts +10 -2
- package/src/server/plugins/engine/options.js +2 -1
- package/src/server/plugins/engine/pageControllers/QuestionPageController.test.ts +1 -0
- package/src/server/plugins/engine/pageControllers/QuestionPageController.ts +69 -1
- package/src/server/plugins/engine/plugin.ts +13 -1
- package/src/server/plugins/engine/routes/index.test.ts +1 -0
- package/src/server/plugins/engine/routes/index.ts +71 -3
- package/src/server/plugins/engine/types.ts +21 -1
- package/src/server/plugins/engine/validationHelpers.ts +48 -0
- package/src/server/plugins/engine/views/components/ukaddressfield.html +50 -6
- package/src/server/plugins/engine/vision.ts +6 -0
- package/src/server/plugins/postcode-lookup/index.js +21 -0
- package/src/server/plugins/postcode-lookup/models/index.js +549 -0
- package/src/server/plugins/postcode-lookup/routes/index.js +258 -0
- package/src/server/plugins/postcode-lookup/service.js +188 -0
- package/src/server/plugins/postcode-lookup/service.test.js +177 -0
- package/src/server/plugins/postcode-lookup/test/__stubs__/postcode.js +382 -0
- package/src/server/plugins/postcode-lookup/test/__stubs__/query.js +200 -0
- package/src/server/plugins/postcode-lookup/test/__stubs__/uprn.js +53 -0
- package/src/server/plugins/postcode-lookup/types.js +143 -0
- package/src/server/plugins/postcode-lookup/views/postcode-lookup-details.html +83 -0
- package/src/server/postcode-lookup.test.ts +64 -0
- package/src/server/routes/types.ts +7 -1
- package/src/server/schemas/index.ts +5 -7
- package/src/server/types.ts +1 -0
|
@@ -56,10 +56,26 @@ pages:
|
|
|
56
56
|
- name: wZLWPy
|
|
57
57
|
options:
|
|
58
58
|
required: true
|
|
59
|
+
usePostcodeLookup: true
|
|
59
60
|
type: UkAddressField
|
|
60
|
-
title:
|
|
61
|
+
title: What is your billing address
|
|
62
|
+
shortDescription: Billing address
|
|
61
63
|
schema: {}
|
|
62
|
-
hint: This is a UK address. Users must enter address line 1, town and a postcode
|
|
64
|
+
hint: This is a UK billing address. Users must enter address line 1, town and a postcode
|
|
65
|
+
- name: dfTGhD
|
|
66
|
+
options: {}
|
|
67
|
+
schema: {}
|
|
68
|
+
type: MultilineTextField
|
|
69
|
+
title: Delivery notes
|
|
70
|
+
hint:
|
|
71
|
+
Enter some instructions for the delivery person
|
|
72
|
+
- name: drGHuj
|
|
73
|
+
options:
|
|
74
|
+
required: true
|
|
75
|
+
type: UkAddressField
|
|
76
|
+
title: What is your delivery address
|
|
77
|
+
schema: {}
|
|
78
|
+
hint: This is a UK delivery address. Users must enter address line 1, town and a postcode
|
|
63
79
|
next:
|
|
64
80
|
- path: '/do-you-want-your-unicorn-breeder-certificate-sent-to-this-address'
|
|
65
81
|
section: section
|
|
@@ -91,6 +91,7 @@ describe('UkAddressField', () => {
|
|
|
91
91
|
|
|
92
92
|
expect(field.keys).toEqual([
|
|
93
93
|
'myComponent',
|
|
94
|
+
'myComponent__uprn',
|
|
94
95
|
'myComponent__addressLine1',
|
|
95
96
|
'myComponent__addressLine2',
|
|
96
97
|
'myComponent__town',
|
|
@@ -194,7 +195,8 @@ describe('UkAddressField', () => {
|
|
|
194
195
|
addressLine2: '',
|
|
195
196
|
town: '',
|
|
196
197
|
county: '',
|
|
197
|
-
postcode: ''
|
|
198
|
+
postcode: '',
|
|
199
|
+
uprn: ''
|
|
198
200
|
})
|
|
199
201
|
)
|
|
200
202
|
|
|
@@ -208,7 +210,8 @@ describe('UkAddressField', () => {
|
|
|
208
210
|
addressLine2: 'Knutsford Road',
|
|
209
211
|
town: 'Warrington',
|
|
210
212
|
county: 'Cheshire',
|
|
211
|
-
postcode: 'WA4 1HT'
|
|
213
|
+
postcode: 'WA4 1HT',
|
|
214
|
+
uprn: ''
|
|
212
215
|
})
|
|
213
216
|
)
|
|
214
217
|
|
|
@@ -218,7 +221,8 @@ describe('UkAddressField', () => {
|
|
|
218
221
|
addressLine2: '', // Optional field
|
|
219
222
|
town: 'Warrington',
|
|
220
223
|
county: '', // Optional field
|
|
221
|
-
postcode: 'WA4 1HT'
|
|
224
|
+
postcode: 'WA4 1HT',
|
|
225
|
+
uprn: ''
|
|
222
226
|
})
|
|
223
227
|
)
|
|
224
228
|
|
|
@@ -233,7 +237,8 @@ describe('UkAddressField', () => {
|
|
|
233
237
|
addressLine2: '',
|
|
234
238
|
town: '',
|
|
235
239
|
county: '',
|
|
236
|
-
postcode: ''
|
|
240
|
+
postcode: '',
|
|
241
|
+
uprn: ''
|
|
237
242
|
})
|
|
238
243
|
)
|
|
239
244
|
|
|
@@ -302,7 +307,7 @@ describe('UkAddressField', () => {
|
|
|
302
307
|
'postal-code'
|
|
303
308
|
]
|
|
304
309
|
|
|
305
|
-
ukAddressField.collection.components.forEach((component) => {
|
|
310
|
+
ukAddressField.collection.components.slice(1).forEach((component) => {
|
|
306
311
|
const addressFieldOptions =
|
|
307
312
|
component.options as TextFieldComponent['options']
|
|
308
313
|
|
|
@@ -319,7 +324,8 @@ describe('UkAddressField', () => {
|
|
|
319
324
|
addressLine2: 'Knutsford Road',
|
|
320
325
|
town: 'Warrington',
|
|
321
326
|
county: 'Cheshire',
|
|
322
|
-
postcode: 'WA4 1HT'
|
|
327
|
+
postcode: 'WA4 1HT',
|
|
328
|
+
uprn: '123456789'
|
|
323
329
|
}
|
|
324
330
|
|
|
325
331
|
it('returns text from state', () => {
|
|
@@ -481,7 +487,8 @@ describe('UkAddressField', () => {
|
|
|
481
487
|
addressLine2: 'Knutsford Road',
|
|
482
488
|
town: 'Warrington',
|
|
483
489
|
county: 'Cheshire',
|
|
484
|
-
postcode: 'WA4 1HT'
|
|
490
|
+
postcode: 'WA4 1HT',
|
|
491
|
+
uprn: ''
|
|
485
492
|
}
|
|
486
493
|
|
|
487
494
|
const addressLine1Invalid =
|
|
@@ -514,7 +521,8 @@ describe('UkAddressField', () => {
|
|
|
514
521
|
addressLine2: ' Knutsford Road',
|
|
515
522
|
town: ' Warrington',
|
|
516
523
|
county: 'Cheshire',
|
|
517
|
-
postcode: ' WA4 1HT'
|
|
524
|
+
postcode: ' WA4 1HT',
|
|
525
|
+
uprn: ''
|
|
518
526
|
}),
|
|
519
527
|
output: {
|
|
520
528
|
value: getFormData(address),
|
|
@@ -527,7 +535,8 @@ describe('UkAddressField', () => {
|
|
|
527
535
|
addressLine2: 'Knutsford Road ',
|
|
528
536
|
town: 'Warrington ',
|
|
529
537
|
county: 'Cheshire ',
|
|
530
|
-
postcode: 'WA4 1HT '
|
|
538
|
+
postcode: 'WA4 1HT ',
|
|
539
|
+
uprn: ''
|
|
531
540
|
}),
|
|
532
541
|
output: {
|
|
533
542
|
value: getFormData(address),
|
|
@@ -540,7 +549,8 @@ describe('UkAddressField', () => {
|
|
|
540
549
|
addressLine2: ' Knutsford Road \n\n',
|
|
541
550
|
town: ' Warrington \n\n',
|
|
542
551
|
county: ' Cheshire \n\n',
|
|
543
|
-
postcode: ' WA4 1HT \n\n'
|
|
552
|
+
postcode: ' WA4 1HT \n\n',
|
|
553
|
+
uprn: ''
|
|
544
554
|
}),
|
|
545
555
|
output: {
|
|
546
556
|
value: getFormData(address),
|
|
@@ -564,7 +574,8 @@ describe('UkAddressField', () => {
|
|
|
564
574
|
addressLine2: 'Knutsford Road',
|
|
565
575
|
town: 'Warrington',
|
|
566
576
|
county: 'Cheshire',
|
|
567
|
-
postcode: 'WA4 1HT'
|
|
577
|
+
postcode: 'WA4 1HT',
|
|
578
|
+
uprn: ''
|
|
568
579
|
}),
|
|
569
580
|
output: {
|
|
570
581
|
value: getFormData({
|
|
@@ -572,7 +583,8 @@ describe('UkAddressField', () => {
|
|
|
572
583
|
addressLine2: 'Knutsford Road',
|
|
573
584
|
town: 'Warrington',
|
|
574
585
|
county: 'Cheshire',
|
|
575
|
-
postcode: 'WA4 1HT'
|
|
586
|
+
postcode: 'WA4 1HT',
|
|
587
|
+
uprn: ''
|
|
576
588
|
}),
|
|
577
589
|
errors: [
|
|
578
590
|
expect.objectContaining({
|
|
@@ -587,7 +599,8 @@ describe('UkAddressField', () => {
|
|
|
587
599
|
addressLine2: addressLine2Invalid,
|
|
588
600
|
town: 'Warrington',
|
|
589
601
|
county: 'Cheshire',
|
|
590
|
-
postcode: 'WA4 1HT'
|
|
602
|
+
postcode: 'WA4 1HT',
|
|
603
|
+
uprn: ''
|
|
591
604
|
}),
|
|
592
605
|
output: {
|
|
593
606
|
value: getFormData({
|
|
@@ -595,7 +608,8 @@ describe('UkAddressField', () => {
|
|
|
595
608
|
addressLine2: addressLine2Invalid,
|
|
596
609
|
town: 'Warrington',
|
|
597
610
|
county: 'Cheshire',
|
|
598
|
-
postcode: 'WA4 1HT'
|
|
611
|
+
postcode: 'WA4 1HT',
|
|
612
|
+
uprn: ''
|
|
599
613
|
}),
|
|
600
614
|
errors: [
|
|
601
615
|
expect.objectContaining({
|
|
@@ -610,7 +624,8 @@ describe('UkAddressField', () => {
|
|
|
610
624
|
addressLine2: 'Knutsford Road',
|
|
611
625
|
town: townInvalid,
|
|
612
626
|
county: 'Cheshire',
|
|
613
|
-
postcode: 'WA4 1HT'
|
|
627
|
+
postcode: 'WA4 1HT',
|
|
628
|
+
uprn: ''
|
|
614
629
|
}),
|
|
615
630
|
output: {
|
|
616
631
|
value: getFormData({
|
|
@@ -618,7 +633,8 @@ describe('UkAddressField', () => {
|
|
|
618
633
|
addressLine2: 'Knutsford Road',
|
|
619
634
|
town: townInvalid,
|
|
620
635
|
county: 'Cheshire',
|
|
621
|
-
postcode: 'WA4 1HT'
|
|
636
|
+
postcode: 'WA4 1HT',
|
|
637
|
+
uprn: ''
|
|
622
638
|
}),
|
|
623
639
|
errors: [
|
|
624
640
|
expect.objectContaining({
|
|
@@ -633,7 +649,8 @@ describe('UkAddressField', () => {
|
|
|
633
649
|
addressLine2: 'Knutsford Road',
|
|
634
650
|
town: 'Warrington',
|
|
635
651
|
county: countyInvalid,
|
|
636
|
-
postcode: 'WA4 1HT'
|
|
652
|
+
postcode: 'WA4 1HT',
|
|
653
|
+
uprn: ''
|
|
637
654
|
}),
|
|
638
655
|
output: {
|
|
639
656
|
value: getFormData({
|
|
@@ -641,7 +658,8 @@ describe('UkAddressField', () => {
|
|
|
641
658
|
addressLine2: 'Knutsford Road',
|
|
642
659
|
town: 'Warrington',
|
|
643
660
|
county: countyInvalid,
|
|
644
|
-
postcode: 'WA4 1HT'
|
|
661
|
+
postcode: 'WA4 1HT',
|
|
662
|
+
uprn: ''
|
|
645
663
|
}),
|
|
646
664
|
errors: [
|
|
647
665
|
expect.objectContaining({
|
|
@@ -656,7 +674,8 @@ describe('UkAddressField', () => {
|
|
|
656
674
|
addressLine2: 'Knutsford Road',
|
|
657
675
|
town: 'Warrington',
|
|
658
676
|
county: 'Cheshire',
|
|
659
|
-
postcode: postcodeInvalid
|
|
677
|
+
postcode: postcodeInvalid,
|
|
678
|
+
uprn: ''
|
|
660
679
|
}),
|
|
661
680
|
output: {
|
|
662
681
|
value: getFormData({
|
|
@@ -664,7 +683,8 @@ describe('UkAddressField', () => {
|
|
|
664
683
|
addressLine2: 'Knutsford Road',
|
|
665
684
|
town: 'Warrington',
|
|
666
685
|
county: 'Cheshire',
|
|
667
|
-
postcode: postcodeInvalid
|
|
686
|
+
postcode: postcodeInvalid,
|
|
687
|
+
uprn: ''
|
|
668
688
|
}),
|
|
669
689
|
errors: [
|
|
670
690
|
expect.objectContaining({
|
|
@@ -679,7 +699,8 @@ describe('UkAddressField', () => {
|
|
|
679
699
|
addressLine2: '',
|
|
680
700
|
town: '',
|
|
681
701
|
county: '',
|
|
682
|
-
postcode: postcodeInvalid
|
|
702
|
+
postcode: postcodeInvalid,
|
|
703
|
+
uprn: ''
|
|
683
704
|
}),
|
|
684
705
|
output: {
|
|
685
706
|
value: getFormData({
|
|
@@ -687,7 +708,8 @@ describe('UkAddressField', () => {
|
|
|
687
708
|
addressLine2: '',
|
|
688
709
|
town: '',
|
|
689
710
|
county: '',
|
|
690
|
-
postcode: postcodeInvalid
|
|
711
|
+
postcode: postcodeInvalid,
|
|
712
|
+
uprn: ''
|
|
691
713
|
}),
|
|
692
714
|
errors: [
|
|
693
715
|
expect.objectContaining({
|
|
@@ -761,7 +783,8 @@ function getFormData(address: FormPayload): FormPayload {
|
|
|
761
783
|
myComponent__addressLine2: address.addressLine2,
|
|
762
784
|
myComponent__town: address.town,
|
|
763
785
|
myComponent__county: address.county,
|
|
764
|
-
myComponent__postcode: address.postcode
|
|
786
|
+
myComponent__postcode: address.postcode,
|
|
787
|
+
myComponent__uprn: address.uprn
|
|
765
788
|
}
|
|
766
789
|
}
|
|
767
790
|
|
|
@@ -769,15 +792,15 @@ function getFormData(address: FormPayload): FormPayload {
|
|
|
769
792
|
* UK address session state
|
|
770
793
|
*/
|
|
771
794
|
function getFormState(address: FormPayload): FormState {
|
|
772
|
-
const [addressLine1, addressLine2, town, county, postcode] =
|
|
773
|
-
getFormData(address)
|
|
774
|
-
)
|
|
795
|
+
const [addressLine1, addressLine2, town, county, postcode, uprn] =
|
|
796
|
+
Object.values(getFormData(address))
|
|
775
797
|
|
|
776
798
|
return {
|
|
777
799
|
myComponent__addressLine1: addressLine1 ?? null,
|
|
778
800
|
myComponent__addressLine2: addressLine2 ?? null,
|
|
779
801
|
myComponent__town: town ?? null,
|
|
780
802
|
myComponent__county: county ?? null,
|
|
781
|
-
myComponent__postcode: postcode ?? null
|
|
803
|
+
myComponent__postcode: postcode ?? null,
|
|
804
|
+
myComponent__uprn: uprn ?? null
|
|
782
805
|
}
|
|
783
806
|
}
|
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
ComponentType,
|
|
3
|
+
type FormComponentsDef,
|
|
4
|
+
type UkAddressFieldComponent
|
|
5
|
+
} from '@defra/forms-model'
|
|
2
6
|
import { type ObjectSchema } from 'joi'
|
|
7
|
+
import lowerFirst from 'lodash/lowerFirst.js'
|
|
3
8
|
|
|
4
9
|
import { ComponentCollection } from '~/src/server/plugins/engine/components/ComponentCollection.js'
|
|
5
10
|
import {
|
|
@@ -8,14 +13,20 @@ import {
|
|
|
8
13
|
} from '~/src/server/plugins/engine/components/FormComponent.js'
|
|
9
14
|
import { TextField } from '~/src/server/plugins/engine/components/TextField.js'
|
|
10
15
|
import { type QuestionPageController } from '~/src/server/plugins/engine/pageControllers/QuestionPageController.js'
|
|
16
|
+
import {
|
|
17
|
+
type FormRequestPayload,
|
|
18
|
+
type FormResponseToolkit
|
|
19
|
+
} from '~/src/server/plugins/engine/types/index.js'
|
|
11
20
|
import {
|
|
12
21
|
type ErrorMessageTemplateList,
|
|
13
22
|
type FormPayload,
|
|
14
23
|
type FormState,
|
|
15
24
|
type FormStateValue,
|
|
16
25
|
type FormSubmissionError,
|
|
17
|
-
type FormSubmissionState
|
|
26
|
+
type FormSubmissionState,
|
|
27
|
+
type PostcodeLookupExternalArgs
|
|
18
28
|
} from '~/src/server/plugins/engine/types.js'
|
|
29
|
+
import { dispatch } from '~/src/server/plugins/postcode-lookup/routes/index.js'
|
|
19
30
|
|
|
20
31
|
export class UkAddressField extends FormComponent {
|
|
21
32
|
declare options: UkAddressFieldComponent['options']
|
|
@@ -23,13 +34,15 @@ export class UkAddressField extends FormComponent {
|
|
|
23
34
|
declare stateSchema: ObjectSchema<FormState>
|
|
24
35
|
declare collection: ComponentCollection
|
|
25
36
|
|
|
37
|
+
shortDescription: FormComponentsDef['shortDescription']
|
|
38
|
+
|
|
26
39
|
constructor(
|
|
27
40
|
def: UkAddressFieldComponent,
|
|
28
41
|
props: ConstructorParameters<typeof FormComponent>[1]
|
|
29
42
|
) {
|
|
30
43
|
super(def, props)
|
|
31
44
|
|
|
32
|
-
const { name, options } = def
|
|
45
|
+
const { name, options, shortDescription } = def
|
|
33
46
|
|
|
34
47
|
const isRequired = options.required !== false
|
|
35
48
|
const hideOptional = !!options.optionalText
|
|
@@ -37,6 +50,16 @@ export class UkAddressField extends FormComponent {
|
|
|
37
50
|
|
|
38
51
|
this.collection = new ComponentCollection(
|
|
39
52
|
[
|
|
53
|
+
{
|
|
54
|
+
type: ComponentType.TextField,
|
|
55
|
+
name: `${name}__uprn`,
|
|
56
|
+
title: 'UPRN',
|
|
57
|
+
schema: {},
|
|
58
|
+
options: {
|
|
59
|
+
required: false,
|
|
60
|
+
classes: 'hidden'
|
|
61
|
+
}
|
|
62
|
+
},
|
|
40
63
|
{
|
|
41
64
|
type: ComponentType.TextField,
|
|
42
65
|
name: `${name}__addressLine1`,
|
|
@@ -103,6 +126,7 @@ export class UkAddressField extends FormComponent {
|
|
|
103
126
|
this.options = options
|
|
104
127
|
this.formSchema = this.collection.formSchema
|
|
105
128
|
this.stateSchema = this.collection.stateSchema
|
|
129
|
+
this.shortDescription = shortDescription
|
|
106
130
|
}
|
|
107
131
|
|
|
108
132
|
getFormValueFromState(state: FormSubmissionState) {
|
|
@@ -115,7 +139,9 @@ export class UkAddressField extends FormComponent {
|
|
|
115
139
|
return null
|
|
116
140
|
}
|
|
117
141
|
|
|
118
|
-
return Object.
|
|
142
|
+
return Object.entries(value)
|
|
143
|
+
.filter(([key, value]) => key !== 'uprn' && Boolean(value))
|
|
144
|
+
.map(([, value]) => value)
|
|
119
145
|
}
|
|
120
146
|
|
|
121
147
|
getContextValueFromState(state: FormSubmissionState) {
|
|
@@ -140,17 +166,34 @@ export class UkAddressField extends FormComponent {
|
|
|
140
166
|
getViewErrors(
|
|
141
167
|
errors?: FormSubmissionError[]
|
|
142
168
|
): FormSubmissionError[] | undefined {
|
|
143
|
-
|
|
169
|
+
const uniqueErrors = this.getErrors(errors)?.filter(
|
|
144
170
|
(error, index, self) =>
|
|
145
171
|
index === self.findIndex((err) => err.name === error.name)
|
|
146
172
|
)
|
|
173
|
+
|
|
174
|
+
// When using postcode lookup, the address fields are hidden
|
|
175
|
+
// so we replace any individual validation messages with a single one
|
|
176
|
+
if (this.shouldUsePostcodeLookup() && uniqueErrors?.length) {
|
|
177
|
+
const { name, shortDescription } = this
|
|
178
|
+
|
|
179
|
+
return [
|
|
180
|
+
{
|
|
181
|
+
name,
|
|
182
|
+
path: [name],
|
|
183
|
+
href: `#${name}`,
|
|
184
|
+
text: `Enter ${lowerFirst(shortDescription)}`
|
|
185
|
+
}
|
|
186
|
+
]
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return uniqueErrors
|
|
147
190
|
}
|
|
148
191
|
|
|
149
192
|
getViewModel(payload: FormPayload, errors?: FormSubmissionError[]) {
|
|
150
193
|
const { collection, name, options } = this
|
|
151
194
|
|
|
152
195
|
const viewModel = super.getViewModel(payload, errors)
|
|
153
|
-
let {
|
|
196
|
+
let { fieldset, hint, label } = viewModel
|
|
154
197
|
|
|
155
198
|
fieldset ??= {
|
|
156
199
|
legend: {
|
|
@@ -173,12 +216,30 @@ export class UkAddressField extends FormComponent {
|
|
|
173
216
|
}
|
|
174
217
|
}
|
|
175
218
|
|
|
176
|
-
components = collection.getViewModel(payload, errors)
|
|
219
|
+
const components = collection.getViewModel(payload, errors)
|
|
220
|
+
|
|
221
|
+
// Hide UPRN
|
|
222
|
+
const uprn = components.at(0)
|
|
223
|
+
|
|
224
|
+
if (!uprn) {
|
|
225
|
+
throw new Error('No UPRN')
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
uprn.model.formGroup = { classes: 'app-hidden' }
|
|
229
|
+
|
|
230
|
+
// Postcode lookup
|
|
231
|
+
const usePostcodeLookup = this.shouldUsePostcodeLookup()
|
|
232
|
+
|
|
233
|
+
const value = usePostcodeLookup
|
|
234
|
+
? this.getDisplayStringFromState(payload)
|
|
235
|
+
: undefined
|
|
177
236
|
|
|
178
237
|
return {
|
|
179
238
|
...viewModel,
|
|
239
|
+
value,
|
|
180
240
|
fieldset,
|
|
181
|
-
components
|
|
241
|
+
components,
|
|
242
|
+
usePostcodeLookup
|
|
182
243
|
}
|
|
183
244
|
}
|
|
184
245
|
|
|
@@ -193,6 +254,10 @@ export class UkAddressField extends FormComponent {
|
|
|
193
254
|
return UkAddressField.getAllPossibleErrors()
|
|
194
255
|
}
|
|
195
256
|
|
|
257
|
+
private shouldUsePostcodeLookup() {
|
|
258
|
+
return !!(this.options.usePostcodeLookup && this.model.ordnanceSurveyApiKey)
|
|
259
|
+
}
|
|
260
|
+
|
|
196
261
|
/**
|
|
197
262
|
* Static version of getAllPossibleErrors that doesn't require a component instance.
|
|
198
263
|
*/
|
|
@@ -218,9 +283,27 @@ export class UkAddressField extends FormComponent {
|
|
|
218
283
|
TextField.isText(value.postcode)
|
|
219
284
|
)
|
|
220
285
|
}
|
|
286
|
+
|
|
287
|
+
static dispatcher(
|
|
288
|
+
request: FormRequestPayload,
|
|
289
|
+
h: FormResponseToolkit,
|
|
290
|
+
args: PostcodeLookupExternalArgs
|
|
291
|
+
) {
|
|
292
|
+
const { controller, component } = args
|
|
293
|
+
|
|
294
|
+
return dispatch(request, h, {
|
|
295
|
+
formName: controller.model.name,
|
|
296
|
+
componentName: component.name,
|
|
297
|
+
componentHint: component.hint,
|
|
298
|
+
componentTitle: component.title || controller.title,
|
|
299
|
+
step: args.actionArgs.step,
|
|
300
|
+
sourceUrl: args.sourceUrl
|
|
301
|
+
})
|
|
302
|
+
}
|
|
221
303
|
}
|
|
222
304
|
|
|
223
305
|
export interface UkAddressState extends Record<string, string> {
|
|
306
|
+
uprn: string
|
|
224
307
|
addressLine1: string
|
|
225
308
|
addressLine2: string
|
|
226
309
|
town: string
|
|
@@ -21,7 +21,8 @@ export const configureEnginePlugin = async (
|
|
|
21
21
|
controllers,
|
|
22
22
|
preparePageEventRequestOptions,
|
|
23
23
|
onRequest,
|
|
24
|
-
saveAndExit
|
|
24
|
+
saveAndExit,
|
|
25
|
+
ordnanceSurveyApiKey
|
|
25
26
|
}: RouteConfig = {},
|
|
26
27
|
cache?: CacheService
|
|
27
28
|
): Promise<{
|
|
@@ -38,7 +39,7 @@ export const configureEnginePlugin = async (
|
|
|
38
39
|
|
|
39
40
|
model = new FormModel(
|
|
40
41
|
definition,
|
|
41
|
-
{ basePath: initialBasePath },
|
|
42
|
+
{ basePath: initialBasePath, ordnanceSurveyApiKey },
|
|
42
43
|
services,
|
|
43
44
|
controllers
|
|
44
45
|
)
|
|
@@ -63,7 +64,8 @@ export const configureEnginePlugin = async (
|
|
|
63
64
|
preparePageEventRequestOptions,
|
|
64
65
|
onRequest,
|
|
65
66
|
baseUrl: 'http://localhost:3009', // always runs locally
|
|
66
|
-
saveAndExit
|
|
67
|
+
saveAndExit,
|
|
68
|
+
ordnanceSurveyApiKey
|
|
67
69
|
}
|
|
68
70
|
}
|
|
69
71
|
}
|
|
@@ -77,6 +77,7 @@ export class FormModel {
|
|
|
77
77
|
values: FormDefinition
|
|
78
78
|
basePath: string
|
|
79
79
|
versionNumber?: number
|
|
80
|
+
ordnanceSurveyApiKey?: string
|
|
80
81
|
conditions: Partial<Record<string, ExecutableCondition>>
|
|
81
82
|
pages: PageControllerClass[]
|
|
82
83
|
services: Services
|
|
@@ -95,7 +96,11 @@ export class FormModel {
|
|
|
95
96
|
|
|
96
97
|
constructor(
|
|
97
98
|
def: typeof this.def,
|
|
98
|
-
options: {
|
|
99
|
+
options: {
|
|
100
|
+
basePath: string
|
|
101
|
+
versionNumber?: number
|
|
102
|
+
ordnanceSurveyApiKey?: string
|
|
103
|
+
},
|
|
99
104
|
services: Services = defaultServices,
|
|
100
105
|
controllers?: Record<string, typeof PageController>
|
|
101
106
|
) {
|
|
@@ -150,6 +155,7 @@ export class FormModel {
|
|
|
150
155
|
this.values = result.value
|
|
151
156
|
this.basePath = options.basePath
|
|
152
157
|
this.versionNumber = options.versionNumber
|
|
158
|
+
this.ordnanceSurveyApiKey = options.ordnanceSurveyApiKey
|
|
153
159
|
this.conditions = {}
|
|
154
160
|
this.services = services
|
|
155
161
|
this.controllers = controllers
|
|
@@ -551,7 +557,9 @@ function validateFormPayload(
|
|
|
551
557
|
// Skip validation GET requests or other actions
|
|
552
558
|
if (
|
|
553
559
|
!request.payload ||
|
|
554
|
-
(action &&
|
|
560
|
+
(action &&
|
|
561
|
+
![FormAction.Validate, FormAction.SaveAndExit].includes(action) &&
|
|
562
|
+
!action.startsWith(FormAction.External))
|
|
555
563
|
) {
|
|
556
564
|
return context
|
|
557
565
|
}
|
|
@@ -25,7 +25,8 @@ const pluginRegistrationOptionsSchema = Joi.object({
|
|
|
25
25
|
preparePageEventRequestOptions: Joi.function().optional(),
|
|
26
26
|
onRequest: Joi.function().optional(),
|
|
27
27
|
baseUrl: Joi.string().uri().required(),
|
|
28
|
-
saveAndExit: Joi.function().optional()
|
|
28
|
+
saveAndExit: Joi.function().optional(),
|
|
29
|
+
ordnanceSurveyApiKey: Joi.string().optional()
|
|
29
30
|
})
|
|
30
31
|
|
|
31
32
|
/**
|
|
@@ -602,6 +602,7 @@ describe('QuestionPageController', () => {
|
|
|
602
602
|
addressField__town: 'Town or city',
|
|
603
603
|
addressField__county: 'Cheshire',
|
|
604
604
|
addressField__postcode: 'CW1 1AB',
|
|
605
|
+
addressField__uprn: '',
|
|
605
606
|
radiosField: 'privateLimitedCompany',
|
|
606
607
|
selectField: 910400000,
|
|
607
608
|
autocompleteField: 910400044,
|
|
@@ -12,6 +12,10 @@ import Boom from '@hapi/boom'
|
|
|
12
12
|
import { type RouteOptions } from '@hapi/hapi'
|
|
13
13
|
import { type ValidationErrorItem } from 'joi'
|
|
14
14
|
|
|
15
|
+
import {
|
|
16
|
+
EXTERNAL_STATE_APPENDAGE,
|
|
17
|
+
EXTERNAL_STATE_PAYLOAD
|
|
18
|
+
} from '~/src/server/constants.js'
|
|
15
19
|
import { ComponentCollection } from '~/src/server/plugins/engine/components/ComponentCollection.js'
|
|
16
20
|
import { optionalText } from '~/src/server/plugins/engine/components/constants.js'
|
|
17
21
|
import { type BackLink } from '~/src/server/plugins/engine/components/types.js'
|
|
@@ -35,6 +39,7 @@ import {
|
|
|
35
39
|
type FormStateValue,
|
|
36
40
|
type FormSubmissionState
|
|
37
41
|
} from '~/src/server/plugins/engine/types.js'
|
|
42
|
+
import { getComponentsByType } from '~/src/server/plugins/engine/validationHelpers.js'
|
|
38
43
|
import {
|
|
39
44
|
FormAction,
|
|
40
45
|
type FormRequest,
|
|
@@ -492,6 +497,11 @@ export class QuestionPageController extends PageController {
|
|
|
492
497
|
) => {
|
|
493
498
|
const { collection, viewName, model } = this
|
|
494
499
|
const { isForceAccess, state, evaluationState } = context
|
|
500
|
+
const action = request.payload.action
|
|
501
|
+
|
|
502
|
+
if (action?.startsWith(FormAction.External)) {
|
|
503
|
+
return this.dispatchExternal(request, h, context)
|
|
504
|
+
}
|
|
495
505
|
|
|
496
506
|
/**
|
|
497
507
|
* If there are any errors, render the page with the parsed errors
|
|
@@ -515,7 +525,6 @@ export class QuestionPageController extends PageController {
|
|
|
515
525
|
await this.setState(request, state)
|
|
516
526
|
|
|
517
527
|
// Check if this is a save-and-exit action
|
|
518
|
-
const { action } = request.payload
|
|
519
528
|
if (action === FormAction.SaveAndExit) {
|
|
520
529
|
return this.handleSaveAndExit(request, context, h)
|
|
521
530
|
}
|
|
@@ -525,6 +534,65 @@ export class QuestionPageController extends PageController {
|
|
|
525
534
|
}
|
|
526
535
|
}
|
|
527
536
|
|
|
537
|
+
private dispatchExternal(
|
|
538
|
+
request: FormRequestPayload,
|
|
539
|
+
h: FormResponseToolkit,
|
|
540
|
+
context: FormContext
|
|
541
|
+
) {
|
|
542
|
+
const { externalComponents } = getComponentsByType()
|
|
543
|
+
const action = request.payload.action ?? ''
|
|
544
|
+
|
|
545
|
+
// Find the external action and arguments
|
|
546
|
+
// `external-{componentName}--{argname1}:{argvalue1}--{argname2}:{argvalue2}`
|
|
547
|
+
// E.g. external-abcdef--amount:10--step:manual
|
|
548
|
+
const externalActionsWithArgs = action
|
|
549
|
+
.slice(`${FormAction.External}-`.length)
|
|
550
|
+
.split('--')
|
|
551
|
+
|
|
552
|
+
const externalActionArgs = externalActionsWithArgs
|
|
553
|
+
.slice(1)
|
|
554
|
+
.map((arg) => arg.split(':'))
|
|
555
|
+
|
|
556
|
+
const args = Object.fromEntries(externalActionArgs) as Record<
|
|
557
|
+
string,
|
|
558
|
+
string
|
|
559
|
+
>
|
|
560
|
+
|
|
561
|
+
const componentName = externalActionsWithArgs[0]
|
|
562
|
+
const component = this.model.componentDefMap.get(componentName)
|
|
563
|
+
const componentType = component?.type
|
|
564
|
+
|
|
565
|
+
if (!componentType) {
|
|
566
|
+
throw Boom.internal(
|
|
567
|
+
`External component of type ${componentType} not found`
|
|
568
|
+
)
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
const selectedComponent = externalComponents.get(componentType)
|
|
572
|
+
|
|
573
|
+
if (!selectedComponent) {
|
|
574
|
+
throw Boom.internal(`External component ${componentName} not found`)
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
// Stash payload without crumb and action
|
|
578
|
+
const stashedPayload = {
|
|
579
|
+
...context.payload,
|
|
580
|
+
crumb: undefined,
|
|
581
|
+
action: undefined
|
|
582
|
+
}
|
|
583
|
+
request.yar.flash(EXTERNAL_STATE_PAYLOAD, stashedPayload, true)
|
|
584
|
+
|
|
585
|
+
// Clear any previous state appendage
|
|
586
|
+
request.yar.clear(EXTERNAL_STATE_APPENDAGE)
|
|
587
|
+
|
|
588
|
+
return selectedComponent.dispatcher(request, h, {
|
|
589
|
+
component,
|
|
590
|
+
controller: this,
|
|
591
|
+
sourceUrl: request.url.toString(),
|
|
592
|
+
actionArgs: args
|
|
593
|
+
})
|
|
594
|
+
}
|
|
595
|
+
|
|
528
596
|
proceed(
|
|
529
597
|
request: FormContextRequest,
|
|
530
598
|
h: FormResponseToolkit,
|