@defra/forms-engine-plugin 4.0.32 → 4.0.34
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/.server/server/constants.d.ts +1 -0
- package/.server/server/constants.js +1 -0
- package/.server/server/constants.js.map +1 -1
- package/.server/server/forms/register-as-a-unicorn-breeder.yaml +0 -1
- package/.server/server/forms/simple-form.yaml +64 -0
- package/.server/server/plugins/engine/beta/form-context.js +1 -2
- package/.server/server/plugins/engine/beta/form-context.js.map +1 -1
- package/.server/server/plugins/engine/components/CheckboxesField.d.ts +1 -1
- package/.server/server/plugins/engine/components/CheckboxesField.js +3 -0
- package/.server/server/plugins/engine/components/CheckboxesField.js.map +1 -1
- package/.server/server/plugins/engine/components/FileUploadField.d.ts +4 -3
- package/.server/server/plugins/engine/components/FileUploadField.js +38 -0
- package/.server/server/plugins/engine/components/FileUploadField.js.map +1 -1
- package/.server/server/plugins/engine/components/FormComponent.d.ts +9 -7
- package/.server/server/plugins/engine/components/FormComponent.js +3 -0
- package/.server/server/plugins/engine/components/FormComponent.js.map +1 -1
- package/.server/server/plugins/engine/helpers.d.ts +5 -0
- package/.server/server/plugins/engine/helpers.js +7 -0
- package/.server/server/plugins/engine/helpers.js.map +1 -1
- package/.server/server/plugins/engine/pageControllers/FileUploadPageController.js +6 -2
- package/.server/server/plugins/engine/pageControllers/FileUploadPageController.js.map +1 -1
- package/.server/server/plugins/engine/pageControllers/QuestionPageController.js +4 -1
- package/.server/server/plugins/engine/pageControllers/QuestionPageController.js.map +1 -1
- package/.server/server/plugins/engine/pageControllers/StartPageController.d.ts +4 -0
- package/.server/server/plugins/engine/pageControllers/SummaryPageController.d.ts +1 -1
- package/.server/server/plugins/engine/pageControllers/SummaryPageController.js +33 -35
- package/.server/server/plugins/engine/pageControllers/SummaryPageController.js.map +1 -1
- package/.server/server/plugins/engine/pageControllers/__stubs__/request.d.ts +2 -2
- package/.server/server/plugins/engine/pageControllers/__stubs__/request.js +9 -0
- package/.server/server/plugins/engine/pageControllers/__stubs__/request.js.map +1 -1
- package/.server/server/plugins/engine/pageControllers/errors.d.ts +15 -0
- package/.server/server/plugins/engine/pageControllers/errors.js +25 -0
- package/.server/server/plugins/engine/pageControllers/errors.js.map +1 -0
- package/.server/server/plugins/engine/services/localFormsService.js +6 -0
- package/.server/server/plugins/engine/services/localFormsService.js.map +1 -1
- package/.server/server/plugins/engine/views/index.html +1 -1
- package/.server/server/plugins/nunjucks/context.test.js +9 -1
- package/.server/server/plugins/nunjucks/context.test.js.map +1 -1
- package/.server/server/plugins/nunjucks/types.d.ts +4 -0
- package/.server/server/plugins/nunjucks/types.js +1 -0
- package/.server/server/plugins/nunjucks/types.js.map +1 -1
- package/.server/server/services/cacheService.d.ts +1 -0
- package/.server/server/services/cacheService.js +10 -0
- package/.server/server/services/cacheService.js.map +1 -1
- package/.server/typings/hapi/index.d.js.map +1 -1
- package/package.json +1 -1
- package/src/server/constants.js +1 -0
- package/src/server/forms/register-as-a-unicorn-breeder.yaml +0 -1
- package/src/server/forms/simple-form.yaml +64 -0
- package/src/server/plugins/engine/beta/form-context.test.ts +4 -3
- package/src/server/plugins/engine/beta/form-context.ts +4 -3
- package/src/server/plugins/engine/components/CheckboxesField.test.ts +38 -18
- package/src/server/plugins/engine/components/CheckboxesField.ts +7 -1
- package/src/server/plugins/engine/components/FileUploadField.test.ts +203 -2
- package/src/server/plugins/engine/components/FileUploadField.ts +61 -2
- package/src/server/plugins/engine/components/FormComponent.ts +17 -1
- package/src/server/plugins/engine/helpers.ts +8 -0
- package/src/server/plugins/engine/pageControllers/FileUploadPageController.test.ts +9 -0
- package/src/server/plugins/engine/pageControllers/FileUploadPageController.ts +11 -4
- package/src/server/plugins/engine/pageControllers/QuestionPageController.ts +6 -0
- package/src/server/plugins/engine/pageControllers/SummaryPageController.test.ts +3 -0
- package/src/server/plugins/engine/pageControllers/SummaryPageController.ts +55 -46
- package/src/server/plugins/engine/pageControllers/__stubs__/request.ts +14 -4
- package/src/server/plugins/engine/pageControllers/errors.test.ts +63 -0
- package/src/server/plugins/engine/pageControllers/errors.ts +30 -0
- package/src/server/plugins/engine/services/localFormsService.js +7 -0
- package/src/server/plugins/engine/views/index.html +1 -1
- package/src/server/plugins/nunjucks/context.test.js +10 -2
- package/src/server/plugins/nunjucks/types.js +1 -0
- package/src/server/services/cacheService.ts +16 -0
- package/src/typings/hapi/index.d.ts +2 -0
|
@@ -2,4 +2,5 @@ export const PREVIEW_PATH_PREFIX = '/preview';
|
|
|
2
2
|
export const FORM_PREFIX = '';
|
|
3
3
|
export const EXTERNAL_STATE_PAYLOAD = 'EXTERNAL_STATE_PAYLOAD';
|
|
4
4
|
export const EXTERNAL_STATE_APPENDAGE = 'EXTERNAL_STATE_APPENDAGE';
|
|
5
|
+
export const COMPONENT_STATE_ERROR = 'COMPONENT_STATE_ERROR';
|
|
5
6
|
//# sourceMappingURL=constants.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.js","names":["PREVIEW_PATH_PREFIX","FORM_PREFIX","EXTERNAL_STATE_PAYLOAD","EXTERNAL_STATE_APPENDAGE"],"sources":["../../src/server/constants.js"],"sourcesContent":["export const PREVIEW_PATH_PREFIX = '/preview'\nexport const FORM_PREFIX = ''\nexport const EXTERNAL_STATE_PAYLOAD = 'EXTERNAL_STATE_PAYLOAD'\nexport const EXTERNAL_STATE_APPENDAGE = 'EXTERNAL_STATE_APPENDAGE'\n"],"mappings":"AAAA,OAAO,MAAMA,mBAAmB,GAAG,UAAU;AAC7C,OAAO,MAAMC,WAAW,GAAG,EAAE;AAC7B,OAAO,MAAMC,sBAAsB,GAAG,wBAAwB;AAC9D,OAAO,MAAMC,wBAAwB,GAAG,0BAA0B","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"constants.js","names":["PREVIEW_PATH_PREFIX","FORM_PREFIX","EXTERNAL_STATE_PAYLOAD","EXTERNAL_STATE_APPENDAGE","COMPONENT_STATE_ERROR"],"sources":["../../src/server/constants.js"],"sourcesContent":["export const PREVIEW_PATH_PREFIX = '/preview'\nexport const FORM_PREFIX = ''\nexport const EXTERNAL_STATE_PAYLOAD = 'EXTERNAL_STATE_PAYLOAD'\nexport const EXTERNAL_STATE_APPENDAGE = 'EXTERNAL_STATE_APPENDAGE'\nexport const COMPONENT_STATE_ERROR = 'COMPONENT_STATE_ERROR'\n"],"mappings":"AAAA,OAAO,MAAMA,mBAAmB,GAAG,UAAU;AAC7C,OAAO,MAAMC,WAAW,GAAG,EAAE;AAC7B,OAAO,MAAMC,sBAAsB,GAAG,wBAAwB;AAC9D,OAAO,MAAMC,wBAAwB,GAAG,0BAA0B;AAClE,OAAO,MAAMC,qBAAqB,GAAG,uBAAuB","ignoreList":[]}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Page events
|
|
3
|
+
engine: V2
|
|
4
|
+
schema: 2
|
|
5
|
+
startPage: '/summary'
|
|
6
|
+
pages:
|
|
7
|
+
- title: Your name
|
|
8
|
+
path: '/your-name'
|
|
9
|
+
components:
|
|
10
|
+
- type: TextField
|
|
11
|
+
title: What is your first name?
|
|
12
|
+
name: applicantFirstName
|
|
13
|
+
shortDescription: Your first name
|
|
14
|
+
hint: ''
|
|
15
|
+
options:
|
|
16
|
+
required: true
|
|
17
|
+
schema: {}
|
|
18
|
+
id: 1fb8e182-c709-4792-8f83-e01d8b1fee1a
|
|
19
|
+
- type: TextField
|
|
20
|
+
title: What is your last name?
|
|
21
|
+
name: applicantLastName
|
|
22
|
+
shortDescription: Your last name
|
|
23
|
+
hint: ''
|
|
24
|
+
options:
|
|
25
|
+
required: true
|
|
26
|
+
schema: {}
|
|
27
|
+
id: b68df7f1-d4f4-4c17-83c8-402f584906c9
|
|
28
|
+
next: []
|
|
29
|
+
id: 622a35ec-3795-418a-81f3-a45746959045
|
|
30
|
+
- title: Upload a copy of your passport
|
|
31
|
+
controller: FileUploadPageController
|
|
32
|
+
path: '/upload-passport'
|
|
33
|
+
components:
|
|
34
|
+
- type: FileUploadField
|
|
35
|
+
title: Please upload a copy of your passport
|
|
36
|
+
name: passportUpload
|
|
37
|
+
shortDescription: Upload passport
|
|
38
|
+
hint: ''
|
|
39
|
+
options:
|
|
40
|
+
required: true
|
|
41
|
+
schema: {}
|
|
42
|
+
id: 987c1234-56d7-89e0-1234-56789abcdef0
|
|
43
|
+
id: 23456789-0abc-def1-2345-67890abcdef1
|
|
44
|
+
- title: ''
|
|
45
|
+
path: '/date-of-birth'
|
|
46
|
+
components:
|
|
47
|
+
- type: DatePartsField
|
|
48
|
+
title: When is {{ applicantFirstName }} {{ applicantLastName }}'s birthday?
|
|
49
|
+
name: dateOfBirth
|
|
50
|
+
shortDescription: Your birthday
|
|
51
|
+
hint: ''
|
|
52
|
+
options:
|
|
53
|
+
required: true
|
|
54
|
+
schema: {}
|
|
55
|
+
id: '00738799-3489-4ab2-a57b-542eecb31bfa'
|
|
56
|
+
next: []
|
|
57
|
+
id: da0fbdb4-a2de-4650-be16-9ba552af135f
|
|
58
|
+
- id: 449a45f6-4541-4a46-91bd-8b8931b07b50
|
|
59
|
+
title: ''
|
|
60
|
+
path: '/summary'
|
|
61
|
+
controller: SummaryPageController
|
|
62
|
+
conditions: []
|
|
63
|
+
sections: []
|
|
64
|
+
lists: []
|
|
@@ -81,8 +81,7 @@ export async function resolveFormModel(server, slug, state, options = {}) {
|
|
|
81
81
|
if (!definition) {
|
|
82
82
|
throw Boom.notFound(`No definition found for form metadata ${metadata.id} (${slug}) ${state}`);
|
|
83
83
|
}
|
|
84
|
-
|
|
85
|
-
checkEmailAddressForLiveFormSubmission(emailAddress, isPreview);
|
|
84
|
+
checkEmailAddressForLiveFormSubmission(metadata.notificationEmail, isPreview);
|
|
86
85
|
const routePrefix = options.routePrefix ?? server.realm.modifiers.route.prefix;
|
|
87
86
|
const model = new FormModel(definition, {
|
|
88
87
|
basePath: options.basePath ?? buildBasePath(routePrefix, slug, formState, isPreview),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"form-context.js","names":["Boom","isEqual","PREVIEW_PATH_PREFIX","checkEmailAddressForLiveFormSubmission","getCacheService","FormModel","TerminalPageController","defaultServices","FormStatus","getFormModel","slug","state","options","services","formsService","isPreview","isPreviewState","formState","resolveState","metadata","getFormMetadata","versionNumber","versions","definition","getFormDefinition","id","notFound","basePath","buildBasePath","routePrefix","ordnanceSurveyApiKey","formId","controllers","getFormContext","server","yar","Live","formModel","resolveFormModel","cacheService","summaryRequest","app","method","params","path","query","url","URL","cachedState","getState","$$__referenceNumber","errors","stateMetadata","models","Map","cache","cacheKey","entry","get","updatedAt","emailAddress","notificationEmail","outputEmail","realm","modifiers","route","prefix","model","set","base","replace","startsWith","slice","getFirstJourneyPage","context","relevantPages","undefined","lastPageReached","at","penultimatePageReached"],"sources":["../../../../../src/server/plugins/engine/beta/form-context.ts"],"sourcesContent":["import Boom from '@hapi/boom'\nimport { type Request, type Server } from '@hapi/hapi'\nimport { isEqual } from 'date-fns'\n\nimport { PREVIEW_PATH_PREFIX } from '~/src/server/constants.js'\nimport {\n checkEmailAddressForLiveFormSubmission,\n getCacheService\n} from '~/src/server/plugins/engine/helpers.js'\nimport { FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport { type PageController } from '~/src/server/plugins/engine/pageControllers/PageController.js'\nimport { TerminalPageController } from '~/src/server/plugins/engine/pageControllers/index.js'\nimport * as defaultServices from '~/src/server/plugins/engine/services/index.js'\nimport {\n type AnyRequest,\n type FormContext,\n type FormContextRequest,\n type FormSubmissionError,\n type FormSubmissionState\n} from '~/src/server/plugins/engine/types.js'\nimport { FormStatus } from '~/src/server/routes/types.js'\nimport { type Services } from '~/src/server/types.js'\n\ntype JourneyState = FormStatus | 'preview'\n\nexport interface FormModelOptions {\n services?: Services\n controllers?: Record<string, typeof PageController>\n basePath?: string\n versionNumber?: number\n ordnanceSurveyApiKey?: string\n formId?: string\n routePrefix?: string\n isPreview?: boolean\n}\n\nexport interface FormContextOptions extends FormModelOptions {\n errors?: FormSubmissionError[]\n}\n\ntype SummaryRequest = FormContextRequest & {\n yar: Request['yar']\n}\n\nexport async function getFormModel(\n slug: string,\n state: JourneyState,\n options: FormModelOptions = {}\n) {\n const services = options.services ?? defaultServices\n const { formsService } = services\n const isPreview = isPreviewState(state, options)\n const formState = resolveState(state)\n\n const metadata = await formsService.getFormMetadata(slug)\n const versionNumber =\n options.versionNumber ?? metadata.versions?.[0]?.versionNumber\n\n const definition = await formsService.getFormDefinition(\n metadata.id,\n formState\n )\n\n if (!definition) {\n throw Boom.notFound(\n `No definition found for form metadata ${metadata.id} (${slug}) ${state}`\n )\n }\n\n return new FormModel(\n definition,\n {\n basePath:\n options.basePath ??\n buildBasePath(options.routePrefix ?? '', slug, formState, isPreview),\n versionNumber,\n ordnanceSurveyApiKey: options.ordnanceSurveyApiKey,\n formId: options.formId ?? metadata.id\n },\n services,\n options.controllers\n )\n}\n\nexport async function getFormContext(\n { server, yar }: Pick<Request, 'server' | 'yar'>,\n slug: string,\n state: JourneyState = FormStatus.Live,\n options: FormContextOptions = {}\n): Promise<FormContext> {\n const formModel = await resolveFormModel(server, slug, state, options)\n\n const cacheService = getCacheService(server)\n\n const summaryRequest: SummaryRequest = {\n app: {},\n method: 'get',\n params: {\n path: 'summary',\n slug,\n ...(isPreviewState(state, options) && {\n state: resolveState(state)\n })\n },\n path: `/${formModel.basePath}/summary`,\n query: {},\n url: new URL(\n `/${formModel.basePath}/summary`,\n 'https://form-context.local'\n ),\n server,\n yar\n }\n\n const cachedState = await cacheService.getState(\n summaryRequest as unknown as AnyRequest\n )\n\n const formState = {\n ...cachedState,\n $$__referenceNumber: cachedState.$$__referenceNumber\n } as unknown as FormSubmissionState\n\n return formModel.getFormContext(\n summaryRequest,\n formState,\n options.errors ?? []\n )\n}\n\nexport async function resolveFormModel(\n server: Server,\n slug: string,\n state: JourneyState,\n options: FormModelOptions = {}\n) {\n const services = options.services ?? defaultServices\n const { formsService } = services\n\n const metadata = await formsService.getFormMetadata(slug)\n const formState = resolveState(state)\n const isPreview = options.isPreview ?? isPreviewState(state, options)\n const stateMetadata = metadata[formState]\n\n if (!stateMetadata) {\n throw Boom.notFound(\n `No '${formState}' state for form metadata ${metadata.id}`\n )\n }\n\n // The models cache is created lazily per server instance\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (!server.app.models) {\n server.app.models = new Map<string, { model: FormModel; updatedAt: Date }>()\n }\n\n const cache = server.app.models as Map<\n string,\n { model: FormModel; updatedAt: Date }\n >\n\n const cacheKey = `${metadata.id}_${formState}_${isPreview}`\n let entry = cache.get(cacheKey)\n\n if (!entry || !isEqual(entry.updatedAt, stateMetadata.updatedAt)) {\n const definition = await formsService.getFormDefinition(\n metadata.id,\n formState\n )\n\n if (!definition) {\n throw Boom.notFound(\n `No definition found for form metadata ${metadata.id} (${slug}) ${state}`\n )\n }\n\n const emailAddress = metadata.notificationEmail ?? definition.outputEmail\n\n checkEmailAddressForLiveFormSubmission(emailAddress, isPreview)\n\n const routePrefix =\n options.routePrefix ?? server.realm.modifiers.route.prefix\n\n const model = new FormModel(\n definition,\n {\n basePath:\n options.basePath ??\n buildBasePath(routePrefix, slug, formState, isPreview),\n versionNumber:\n options.versionNumber ?? metadata.versions?.[0]?.versionNumber,\n ordnanceSurveyApiKey: options.ordnanceSurveyApiKey,\n formId: options.formId ?? metadata.id\n },\n services,\n options.controllers\n )\n\n entry = { model, updatedAt: stateMetadata.updatedAt }\n cache.set(cacheKey, entry)\n }\n\n return entry.model\n}\n\nfunction buildBasePath(\n routePrefix: string,\n slug: string,\n state: FormStatus,\n isPreview: boolean\n) {\n const base = (\n isPreview\n ? `${routePrefix}${PREVIEW_PATH_PREFIX}/${state}/${slug}`\n : `${routePrefix}/${slug}`\n ).replace(/\\/{2,}/g, '/')\n\n return base.startsWith('/') ? base.slice(1) : base\n}\n\nexport function getFirstJourneyPage(\n context?: Pick<FormContext, 'relevantPages'>\n) {\n if (!context?.relevantPages) {\n return undefined\n }\n\n const lastPageReached = context.relevantPages.at(-1)\n const penultimatePageReached = context.relevantPages.at(-2)\n\n if (\n lastPageReached instanceof TerminalPageController &&\n penultimatePageReached\n ) {\n return penultimatePageReached\n }\n\n return lastPageReached\n}\n\nfunction resolveState(state: JourneyState): FormStatus {\n return state === 'preview' ? FormStatus.Live : state\n}\n\nfunction isPreviewState(\n state: JourneyState,\n options: FormModelOptions = {}\n): boolean {\n return options.isPreview ?? state === 'preview'\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"form-context.js","names":["Boom","isEqual","PREVIEW_PATH_PREFIX","checkEmailAddressForLiveFormSubmission","getCacheService","FormModel","TerminalPageController","defaultServices","FormStatus","getFormModel","slug","state","options","services","formsService","isPreview","isPreviewState","formState","resolveState","metadata","getFormMetadata","versionNumber","versions","definition","getFormDefinition","id","notFound","basePath","buildBasePath","routePrefix","ordnanceSurveyApiKey","formId","controllers","getFormContext","server","yar","Live","formModel","resolveFormModel","cacheService","summaryRequest","app","method","params","path","query","url","URL","cachedState","getState","$$__referenceNumber","errors","stateMetadata","models","Map","cache","cacheKey","entry","get","updatedAt","notificationEmail","realm","modifiers","route","prefix","model","set","base","replace","startsWith","slice","getFirstJourneyPage","context","relevantPages","undefined","lastPageReached","at","penultimatePageReached"],"sources":["../../../../../src/server/plugins/engine/beta/form-context.ts"],"sourcesContent":["import Boom from '@hapi/boom'\nimport { type Request, type Server } from '@hapi/hapi'\nimport { isEqual } from 'date-fns'\n\nimport { PREVIEW_PATH_PREFIX } from '~/src/server/constants.js'\nimport {\n checkEmailAddressForLiveFormSubmission,\n getCacheService\n} from '~/src/server/plugins/engine/helpers.js'\nimport { FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport { type PageController } from '~/src/server/plugins/engine/pageControllers/PageController.js'\nimport { TerminalPageController } from '~/src/server/plugins/engine/pageControllers/index.js'\nimport * as defaultServices from '~/src/server/plugins/engine/services/index.js'\nimport {\n type AnyRequest,\n type FormContext,\n type FormContextRequest,\n type FormSubmissionError,\n type FormSubmissionState\n} from '~/src/server/plugins/engine/types.js'\nimport { FormStatus } from '~/src/server/routes/types.js'\nimport { type Services } from '~/src/server/types.js'\n\ntype JourneyState = FormStatus | 'preview'\n\nexport interface FormModelOptions {\n services?: Services\n controllers?: Record<string, typeof PageController>\n basePath?: string\n versionNumber?: number\n ordnanceSurveyApiKey?: string\n formId?: string\n routePrefix?: string\n isPreview?: boolean\n}\n\nexport interface FormContextOptions extends FormModelOptions {\n errors?: FormSubmissionError[]\n}\n\ntype SummaryRequest = FormContextRequest & {\n yar: Request['yar']\n}\n\nexport async function getFormModel(\n slug: string,\n state: JourneyState,\n options: FormModelOptions = {}\n) {\n const services = options.services ?? defaultServices\n const { formsService } = services\n const isPreview = isPreviewState(state, options)\n const formState = resolveState(state)\n\n const metadata = await formsService.getFormMetadata(slug)\n const versionNumber =\n options.versionNumber ?? metadata.versions?.[0]?.versionNumber\n\n const definition = await formsService.getFormDefinition(\n metadata.id,\n formState\n )\n\n if (!definition) {\n throw Boom.notFound(\n `No definition found for form metadata ${metadata.id} (${slug}) ${state}`\n )\n }\n\n return new FormModel(\n definition,\n {\n basePath:\n options.basePath ??\n buildBasePath(options.routePrefix ?? '', slug, formState, isPreview),\n versionNumber,\n ordnanceSurveyApiKey: options.ordnanceSurveyApiKey,\n formId: options.formId ?? metadata.id\n },\n services,\n options.controllers\n )\n}\n\nexport async function getFormContext(\n { server, yar }: Pick<Request, 'server' | 'yar'>,\n slug: string,\n state: JourneyState = FormStatus.Live,\n options: FormContextOptions = {}\n): Promise<FormContext> {\n const formModel = await resolveFormModel(server, slug, state, options)\n\n const cacheService = getCacheService(server)\n\n const summaryRequest: SummaryRequest = {\n app: {},\n method: 'get',\n params: {\n path: 'summary',\n slug,\n ...(isPreviewState(state, options) && {\n state: resolveState(state)\n })\n },\n path: `/${formModel.basePath}/summary`,\n query: {},\n url: new URL(\n `/${formModel.basePath}/summary`,\n 'https://form-context.local'\n ),\n server,\n yar\n }\n\n const cachedState = await cacheService.getState(\n summaryRequest as unknown as AnyRequest\n )\n\n const formState = {\n ...cachedState,\n $$__referenceNumber: cachedState.$$__referenceNumber\n } as unknown as FormSubmissionState\n\n return formModel.getFormContext(\n summaryRequest,\n formState,\n options.errors ?? []\n )\n}\n\nexport async function resolveFormModel(\n server: Server,\n slug: string,\n state: JourneyState,\n options: FormModelOptions = {}\n) {\n const services = options.services ?? defaultServices\n const { formsService } = services\n\n const metadata = await formsService.getFormMetadata(slug)\n const formState = resolveState(state)\n const isPreview = options.isPreview ?? isPreviewState(state, options)\n const stateMetadata = metadata[formState]\n\n if (!stateMetadata) {\n throw Boom.notFound(\n `No '${formState}' state for form metadata ${metadata.id}`\n )\n }\n\n // The models cache is created lazily per server instance\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (!server.app.models) {\n server.app.models = new Map<string, { model: FormModel; updatedAt: Date }>()\n }\n\n const cache = server.app.models as Map<\n string,\n { model: FormModel; updatedAt: Date }\n >\n\n const cacheKey = `${metadata.id}_${formState}_${isPreview}`\n let entry = cache.get(cacheKey)\n\n if (!entry || !isEqual(entry.updatedAt, stateMetadata.updatedAt)) {\n const definition = await formsService.getFormDefinition(\n metadata.id,\n formState\n )\n\n if (!definition) {\n throw Boom.notFound(\n `No definition found for form metadata ${metadata.id} (${slug}) ${state}`\n )\n }\n\n checkEmailAddressForLiveFormSubmission(\n metadata.notificationEmail,\n isPreview\n )\n\n const routePrefix =\n options.routePrefix ?? server.realm.modifiers.route.prefix\n\n const model = new FormModel(\n definition,\n {\n basePath:\n options.basePath ??\n buildBasePath(routePrefix, slug, formState, isPreview),\n versionNumber:\n options.versionNumber ?? metadata.versions?.[0]?.versionNumber,\n ordnanceSurveyApiKey: options.ordnanceSurveyApiKey,\n formId: options.formId ?? metadata.id\n },\n services,\n options.controllers\n )\n\n entry = { model, updatedAt: stateMetadata.updatedAt }\n cache.set(cacheKey, entry)\n }\n\n return entry.model\n}\n\nfunction buildBasePath(\n routePrefix: string,\n slug: string,\n state: FormStatus,\n isPreview: boolean\n) {\n const base = (\n isPreview\n ? `${routePrefix}${PREVIEW_PATH_PREFIX}/${state}/${slug}`\n : `${routePrefix}/${slug}`\n ).replace(/\\/{2,}/g, '/')\n\n return base.startsWith('/') ? base.slice(1) : base\n}\n\nexport function getFirstJourneyPage(\n context?: Pick<FormContext, 'relevantPages'>\n) {\n if (!context?.relevantPages) {\n return undefined\n }\n\n const lastPageReached = context.relevantPages.at(-1)\n const penultimatePageReached = context.relevantPages.at(-2)\n\n if (\n lastPageReached instanceof TerminalPageController &&\n penultimatePageReached\n ) {\n return penultimatePageReached\n }\n\n return lastPageReached\n}\n\nfunction resolveState(state: JourneyState): FormStatus {\n return state === 'preview' ? FormStatus.Live : state\n}\n\nfunction isPreviewState(\n state: JourneyState,\n options: FormModelOptions = {}\n): boolean {\n return options.isPreview ?? state === 'preview'\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -9,7 +9,7 @@ export declare class CheckboxesField extends SelectionControlField {
|
|
|
9
9
|
constructor(def: CheckboxesFieldComponent, props: ConstructorParameters<typeof SelectionControlField>[1]);
|
|
10
10
|
getFormValueFromState(state: FormSubmissionState): (string | number | boolean)[] | undefined;
|
|
11
11
|
getFormValue(value?: FormStateValue | FormState): (string | number | boolean)[] | undefined;
|
|
12
|
-
getDisplayStringFromFormValue(selected: (string | number | boolean)[]): string;
|
|
12
|
+
getDisplayStringFromFormValue(selected: (string | number | boolean)[] | undefined): string;
|
|
13
13
|
getContextValueFromFormValue(values: (string | number | boolean)[] | undefined): (string | number | boolean)[];
|
|
14
14
|
getDisplayStringFromState(state: FormSubmissionState): string;
|
|
15
15
|
getContextValueFromState(state: FormSubmissionState): (string | number | boolean)[];
|
|
@@ -40,6 +40,9 @@ export class CheckboxesField extends SelectionControlField {
|
|
|
40
40
|
const {
|
|
41
41
|
items
|
|
42
42
|
} = this;
|
|
43
|
+
if (!selected) {
|
|
44
|
+
return '';
|
|
45
|
+
}
|
|
43
46
|
|
|
44
47
|
// Map selected values to text
|
|
45
48
|
return items.filter(item => selected.includes(item.value)).map(item => item.text).join(', ');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CheckboxesField.js","names":["joi","isFormValue","SelectionControlField","CheckboxesField","constructor","def","props","listType","type","options","formSchema","array","itemsSchema","valid","values","label","items","single","required","optional","default","stateSchema","allow","getFormValueFromState","state","name","getFormValue","selected","filter","item","includes","value","map","length","undefined","isValue","getDisplayStringFromFormValue","text","join","getContextValueFromFormValue","getDisplayStringFromState","getContextValueFromState","Array","isArray","every"],"sources":["../../../../../src/server/plugins/engine/components/CheckboxesField.ts"],"sourcesContent":["import { type CheckboxesFieldComponent, type Item } from '@defra/forms-model'\nimport joi, { type ArraySchema } from 'joi'\n\nimport { isFormValue } from '~/src/server/plugins/engine/components/FormComponent.js'\nimport { SelectionControlField } from '~/src/server/plugins/engine/components/SelectionControlField.js'\nimport { type FormModel } from '~/src/server/plugins/engine/models/FormModel.js'\nimport { type QuestionPageController } from '~/src/server/plugins/engine/pageControllers/QuestionPageController.js'\nimport {\n type FormState,\n type FormStateValue,\n type FormSubmissionState\n} from '~/src/server/plugins/engine/types.js'\n\nexport class CheckboxesField extends SelectionControlField {\n declare options: CheckboxesFieldComponent['options']\n declare formSchema: ArraySchema<string> | ArraySchema<number>\n declare stateSchema: ArraySchema<string> | ArraySchema<number>\n\n constructor(\n def: CheckboxesFieldComponent,\n props: ConstructorParameters<typeof SelectionControlField>[1]\n ) {\n super(def, props)\n\n const { listType: type } = this\n const { options } = def\n\n let formSchema =\n type === 'string' ? joi.array<string>() : joi.array<number>()\n\n const itemsSchema = joi[type]()\n .valid(...this.values)\n .label(this.label)\n\n formSchema = formSchema\n .items(itemsSchema)\n .single()\n .label(this.label)\n .required()\n\n if (options.required === false) {\n formSchema = formSchema.optional()\n }\n\n this.formSchema = formSchema.default([])\n this.stateSchema = formSchema.default(null).allow(null)\n this.options = options\n }\n\n getFormValueFromState(state: FormSubmissionState) {\n const { items, name } = this\n\n // State checkbox values\n const values = this.getFormValue(state[name]) ?? []\n\n // Map (or discard) state values to item values\n const selected = items\n .filter((item) => values.includes(item.value))\n .map((item) => item.value)\n\n return selected.length ? selected : undefined\n }\n\n getFormValue(value?: FormStateValue | FormState) {\n return this.isValue(value) ? value : undefined\n }\n\n getDisplayStringFromFormValue(selected: (string | number | boolean)[]) {\n const { items } = this\n\n // Map selected values to text\n return items\n .filter((item) => selected.includes(item.value))\n .map((item) => item.text)\n .join(', ')\n }\n\n getContextValueFromFormValue(\n values: (string | number | boolean)[] | undefined\n ): (string | number | boolean)[] {\n /**\n * For evaluation context purposes, optional {@link CheckboxesField}\n * with an undefined value (i.e. nothing selected) should default to [].\n * This way conditions are not evaluated against `undefined` which throws errors.\n * Currently these errors are caught and the evaluation returns default `false`.\n * @see {@link QuestionPageController.getNextPath} for `undefined` return value\n * @see {@link FormModel.makeCondition} for try/catch block with default `false`\n * For negative conditions this is a problem because E.g.\n * The condition: 'selectedchecks' does not contain 'someval'\n * should return true IF 'selectedchecks' is undefined, not throw and return false.\n */\n return values ?? []\n }\n\n getDisplayStringFromState(state: FormSubmissionState) {\n // Selected checkbox values\n const selected = this.getFormValueFromState(state) ?? []\n\n // Map selected values to text\n return this.getDisplayStringFromFormValue(selected)\n }\n\n getContextValueFromState(state: FormSubmissionState) {\n const values = this.getFormValueFromState(state)\n\n return this.getContextValueFromFormValue(values)\n }\n\n isValue(value?: FormStateValue | FormState): value is Item['value'][] {\n if (!Array.isArray(value)) {\n return false\n }\n\n // Skip checks when empty\n if (!value.length) {\n return true\n }\n\n return value.every(isFormValue)\n }\n}\n"],"mappings":"AACA,OAAOA,GAAG,MAA4B,KAAK;AAE3C,SAASC,WAAW;AACpB,SAASC,qBAAqB;AAS9B,OAAO,MAAMC,eAAe,SAASD,qBAAqB,CAAC;EAKzDE,WAAWA,CACTC,GAA6B,EAC7BC,KAA6D,EAC7D;IACA,KAAK,CAACD,GAAG,EAAEC,KAAK,CAAC;IAEjB,MAAM;MAAEC,QAAQ,EAAEC;IAAK,CAAC,GAAG,IAAI;IAC/B,MAAM;MAAEC;IAAQ,CAAC,GAAGJ,GAAG;IAEvB,IAAIK,UAAU,GACZF,IAAI,KAAK,QAAQ,GAAGR,GAAG,CAACW,KAAK,CAAS,CAAC,GAAGX,GAAG,CAACW,KAAK,CAAS,CAAC;IAE/D,MAAMC,WAAW,GAAGZ,GAAG,CAACQ,IAAI,CAAC,CAAC,CAAC,CAC5BK,KAAK,CAAC,GAAG,IAAI,CAACC,MAAM,CAAC,CACrBC,KAAK,CAAC,IAAI,CAACA,KAAK,CAAC;IAEpBL,UAAU,GAAGA,UAAU,CACpBM,KAAK,CAACJ,WAAW,CAAC,CAClBK,MAAM,CAAC,CAAC,CACRF,KAAK,CAAC,IAAI,CAACA,KAAK,CAAC,CACjBG,QAAQ,CAAC,CAAC;IAEb,IAAIT,OAAO,CAACS,QAAQ,KAAK,KAAK,EAAE;MAC9BR,UAAU,GAAGA,UAAU,CAACS,QAAQ,CAAC,CAAC;IACpC;IAEA,IAAI,CAACT,UAAU,GAAGA,UAAU,CAACU,OAAO,CAAC,EAAE,CAAC;IACxC,IAAI,CAACC,WAAW,GAAGX,UAAU,CAACU,OAAO,CAAC,IAAI,CAAC,CAACE,KAAK,CAAC,IAAI,CAAC;IACvD,IAAI,CAACb,OAAO,GAAGA,OAAO;EACxB;EAEAc,qBAAqBA,CAACC,KAA0B,EAAE;IAChD,MAAM;MAAER,KAAK;MAAES;IAAK,CAAC,GAAG,IAAI;;IAE5B;IACA,MAAMX,MAAM,GAAG,IAAI,CAACY,YAAY,CAACF,KAAK,CAACC,IAAI,CAAC,CAAC,IAAI,EAAE;;IAEnD;IACA,MAAME,QAAQ,GAAGX,KAAK,CACnBY,MAAM,CAAEC,IAAI,IAAKf,MAAM,CAACgB,QAAQ,CAACD,IAAI,CAACE,KAAK,CAAC,CAAC,CAC7CC,GAAG,CAAEH,IAAI,IAAKA,IAAI,CAACE,KAAK,CAAC;IAE5B,OAAOJ,QAAQ,CAACM,MAAM,GAAGN,QAAQ,GAAGO,SAAS;EAC/C;EAEAR,YAAYA,CAACK,KAAkC,EAAE;IAC/C,OAAO,IAAI,CAACI,OAAO,CAACJ,KAAK,CAAC,GAAGA,KAAK,GAAGG,SAAS;EAChD;EAEAE,6BAA6BA,
|
|
1
|
+
{"version":3,"file":"CheckboxesField.js","names":["joi","isFormValue","SelectionControlField","CheckboxesField","constructor","def","props","listType","type","options","formSchema","array","itemsSchema","valid","values","label","items","single","required","optional","default","stateSchema","allow","getFormValueFromState","state","name","getFormValue","selected","filter","item","includes","value","map","length","undefined","isValue","getDisplayStringFromFormValue","text","join","getContextValueFromFormValue","getDisplayStringFromState","getContextValueFromState","Array","isArray","every"],"sources":["../../../../../src/server/plugins/engine/components/CheckboxesField.ts"],"sourcesContent":["import { type CheckboxesFieldComponent, type Item } from '@defra/forms-model'\nimport joi, { type ArraySchema } from 'joi'\n\nimport { isFormValue } from '~/src/server/plugins/engine/components/FormComponent.js'\nimport { SelectionControlField } from '~/src/server/plugins/engine/components/SelectionControlField.js'\nimport { type FormModel } from '~/src/server/plugins/engine/models/FormModel.js'\nimport { type QuestionPageController } from '~/src/server/plugins/engine/pageControllers/QuestionPageController.js'\nimport {\n type FormState,\n type FormStateValue,\n type FormSubmissionState\n} from '~/src/server/plugins/engine/types.js'\n\nexport class CheckboxesField extends SelectionControlField {\n declare options: CheckboxesFieldComponent['options']\n declare formSchema: ArraySchema<string> | ArraySchema<number>\n declare stateSchema: ArraySchema<string> | ArraySchema<number>\n\n constructor(\n def: CheckboxesFieldComponent,\n props: ConstructorParameters<typeof SelectionControlField>[1]\n ) {\n super(def, props)\n\n const { listType: type } = this\n const { options } = def\n\n let formSchema =\n type === 'string' ? joi.array<string>() : joi.array<number>()\n\n const itemsSchema = joi[type]()\n .valid(...this.values)\n .label(this.label)\n\n formSchema = formSchema\n .items(itemsSchema)\n .single()\n .label(this.label)\n .required()\n\n if (options.required === false) {\n formSchema = formSchema.optional()\n }\n\n this.formSchema = formSchema.default([])\n this.stateSchema = formSchema.default(null).allow(null)\n this.options = options\n }\n\n getFormValueFromState(state: FormSubmissionState) {\n const { items, name } = this\n\n // State checkbox values\n const values = this.getFormValue(state[name]) ?? []\n\n // Map (or discard) state values to item values\n const selected = items\n .filter((item) => values.includes(item.value))\n .map((item) => item.value)\n\n return selected.length ? selected : undefined\n }\n\n getFormValue(value?: FormStateValue | FormState) {\n return this.isValue(value) ? value : undefined\n }\n\n getDisplayStringFromFormValue(\n selected: (string | number | boolean)[] | undefined\n ) {\n const { items } = this\n\n if (!selected) {\n return ''\n }\n\n // Map selected values to text\n return items\n .filter((item) => selected.includes(item.value))\n .map((item) => item.text)\n .join(', ')\n }\n\n getContextValueFromFormValue(\n values: (string | number | boolean)[] | undefined\n ): (string | number | boolean)[] {\n /**\n * For evaluation context purposes, optional {@link CheckboxesField}\n * with an undefined value (i.e. nothing selected) should default to [].\n * This way conditions are not evaluated against `undefined` which throws errors.\n * Currently these errors are caught and the evaluation returns default `false`.\n * @see {@link QuestionPageController.getNextPath} for `undefined` return value\n * @see {@link FormModel.makeCondition} for try/catch block with default `false`\n * For negative conditions this is a problem because E.g.\n * The condition: 'selectedchecks' does not contain 'someval'\n * should return true IF 'selectedchecks' is undefined, not throw and return false.\n */\n return values ?? []\n }\n\n getDisplayStringFromState(state: FormSubmissionState) {\n // Selected checkbox values\n const selected = this.getFormValueFromState(state) ?? []\n\n // Map selected values to text\n return this.getDisplayStringFromFormValue(selected)\n }\n\n getContextValueFromState(state: FormSubmissionState) {\n const values = this.getFormValueFromState(state)\n\n return this.getContextValueFromFormValue(values)\n }\n\n isValue(value?: FormStateValue | FormState): value is Item['value'][] {\n if (!Array.isArray(value)) {\n return false\n }\n\n // Skip checks when empty\n if (!value.length) {\n return true\n }\n\n return value.every(isFormValue)\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { type FileUploadFieldComponent } from '@defra/forms-model';
|
|
1
|
+
import { type FileUploadFieldComponent, type FormMetadata } from '@defra/forms-model';
|
|
2
2
|
import joi, { type ArraySchema } from 'joi';
|
|
3
3
|
import { FormComponent } from '~/src/server/plugins/engine/components/FormComponent.js';
|
|
4
|
-
import { type ErrorMessageTemplateList, type FileState, type FileUpload, type FileUploadMetadata, type FormPayload, type FormState, type FormStateValue, type FormSubmissionError, type FormSubmissionState, type SummaryList, type UploadState, type UploadStatusFileResponse, type UploadStatusResponse } from '~/src/server/plugins/engine/types.js';
|
|
5
|
-
import { type FormQuery } from '~/src/server/routes/types.js';
|
|
4
|
+
import { type ErrorMessageTemplateList, type FileState, type FileUpload, type FileUploadMetadata, type FormContext, type FormPayload, type FormState, type FormStateValue, type FormSubmissionError, type FormSubmissionState, type SummaryList, type UploadState, type UploadStatusFileResponse, type UploadStatusResponse } from '~/src/server/plugins/engine/types.js';
|
|
5
|
+
import { type FormQuery, type FormRequestPayload } from '~/src/server/routes/types.js';
|
|
6
6
|
export declare const uploadIdSchema: joi.StringSchema<string>;
|
|
7
7
|
export declare const fileSchema: joi.ObjectSchema<FileUpload>;
|
|
8
8
|
export declare const tempFileSchema: joi.ObjectSchema<FileUpload>;
|
|
@@ -79,6 +79,7 @@ export declare class FileUploadField extends FormComponent {
|
|
|
79
79
|
* For error preview page that shows all possible errors on a component
|
|
80
80
|
*/
|
|
81
81
|
getAllPossibleErrors(): ErrorMessageTemplateList;
|
|
82
|
+
onSubmit(request: FormRequestPayload, metadata: FormMetadata, context: FormContext): Promise<void>;
|
|
82
83
|
/**
|
|
83
84
|
* Static version of getAllPossibleErrors that doesn't require a component instance.
|
|
84
85
|
*/
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import Boom from '@hapi/boom';
|
|
1
2
|
import joi from 'joi';
|
|
2
3
|
import { FormComponent, isUploadState } from "./FormComponent.js";
|
|
4
|
+
import { InvalidComponentStateError } from "../pageControllers/errors.js";
|
|
3
5
|
import { messageTemplate } from "../pageControllers/validationOptions.js";
|
|
4
6
|
import { FileStatus, UploadStatus } from "../types.js";
|
|
5
7
|
import { render } from "../../nunjucks/index.js";
|
|
@@ -204,6 +206,42 @@ export class FileUploadField extends FormComponent {
|
|
|
204
206
|
getAllPossibleErrors() {
|
|
205
207
|
return FileUploadField.getAllPossibleErrors();
|
|
206
208
|
}
|
|
209
|
+
async onSubmit(request, metadata, context) {
|
|
210
|
+
const notificationEmail = metadata.notificationEmail;
|
|
211
|
+
if (!notificationEmail) {
|
|
212
|
+
// this should not happen because notificationEmail is checked further up
|
|
213
|
+
// the chain in SummaryPageController before submitForm is called.
|
|
214
|
+
throw new Error('Unexpected missing notificationEmail in metadata');
|
|
215
|
+
}
|
|
216
|
+
if (!request.app.model?.services.formSubmissionService) {
|
|
217
|
+
throw new Error('No form submission service available in app model');
|
|
218
|
+
}
|
|
219
|
+
const {
|
|
220
|
+
formSubmissionService
|
|
221
|
+
} = request.app.model.services;
|
|
222
|
+
const values = this.getFormValueFromState(context.state) ?? [];
|
|
223
|
+
const files = values.map(value => ({
|
|
224
|
+
fileId: value.status.form.file.fileId,
|
|
225
|
+
initiatedRetrievalKey: value.status.metadata.retrievalKey
|
|
226
|
+
}));
|
|
227
|
+
if (!files.length) {
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
try {
|
|
231
|
+
await formSubmissionService.persistFiles(files, notificationEmail);
|
|
232
|
+
} catch (error) {
|
|
233
|
+
if (Boom.isBoom(error) && (error.output.statusCode === 403 ||
|
|
234
|
+
// Forbidden - retrieval key invalid
|
|
235
|
+
error.output.statusCode === 410) // Gone - file expired (took to long to submit, etc)
|
|
236
|
+
) {
|
|
237
|
+
// Failed to persist files. We can't recover from this, the only real way we can recover the submissions is
|
|
238
|
+
// by resetting the problematic components and letting the user re-try.
|
|
239
|
+
// Scenarios: file missing from S3, invalid retrieval key (timing problem), etc.
|
|
240
|
+
throw new InvalidComponentStateError(this, 'There was a problem with your uploaded files. Re-upload them before submitting the form again.');
|
|
241
|
+
}
|
|
242
|
+
throw error;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
207
245
|
|
|
208
246
|
/**
|
|
209
247
|
* Static version of getAllPossibleErrors that doesn't require a component instance.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FileUploadField.js","names":["joi","FormComponent","isUploadState","messageTemplate","FileStatus","UploadStatus","render","uploadIdSchema","string","uuid","required","fileSchema","object","fileId","filename","contentLength","number","tempFileSchema","append","fileStatus","valid","complete","rejected","pending","errorMessage","optional","formFileSchema","metadataSchema","keys","retrievalKey","email","tempStatusSchema","uploadStatus","ready","metadata","form","file","numberOfRejectedFiles","formStatusSchema","itemSchema","uploadId","tempItemSchema","status","formItemSchema","FileUploadField","constructor","def","props","options","schema","formSchema","array","label","single","length","max","min","items","stateSchema","default","allow","getFormValueFromState","state","name","getFormValue","value","isValue","undefined","getDisplayStringFromFormValue","files","unit","getDisplayStringFromState","getContextValueFromFormValue","map","getContextValueFromState","getViewModel","payload","errors","query","page","isForceAccess","viewModel","attributes","id","filtered","filter","count","rows","item","index","tag","classes","text","valueHtml","view","context","params","trim","keyHtml","path","href","getHref","push","visuallyHiddenText","key","html","actions","accept","summaryList","upload","getAllPossibleErrors","baseErrors","type","template","selectRequired","advancedSettingsErrors"],"sources":["../../../../../src/server/plugins/engine/components/FileUploadField.ts"],"sourcesContent":["import { type FileUploadFieldComponent } from '@defra/forms-model'\nimport joi, { type ArraySchema } from 'joi'\n\nimport {\n FormComponent,\n isUploadState\n} from '~/src/server/plugins/engine/components/FormComponent.js'\nimport { messageTemplate } from '~/src/server/plugins/engine/pageControllers/validationOptions.js'\nimport {\n FileStatus,\n UploadStatus,\n type ErrorMessageTemplateList,\n type FileState,\n type FileUpload,\n type FileUploadMetadata,\n type FormPayload,\n type FormState,\n type FormStateValue,\n type FormSubmissionError,\n type FormSubmissionState,\n type SummaryList,\n type SummaryListAction,\n type SummaryListRow,\n type UploadState,\n type UploadStatusFileResponse,\n type UploadStatusResponse\n} from '~/src/server/plugins/engine/types.js'\nimport { render } from '~/src/server/plugins/nunjucks/index.js'\nimport { type FormQuery } from '~/src/server/routes/types.js'\n\nexport const uploadIdSchema = joi.string().uuid().required()\n\nexport const fileSchema = joi\n .object<FileUpload>({\n fileId: joi.string().uuid().required(),\n filename: joi.string().required(),\n contentLength: joi.number().required()\n })\n .required()\n\nexport const tempFileSchema = fileSchema.append({\n fileStatus: joi\n .string()\n .valid(FileStatus.complete, FileStatus.rejected, FileStatus.pending)\n .required(),\n errorMessage: joi.string().optional()\n})\n\nexport const formFileSchema = fileSchema.append({\n fileStatus: joi.string().valid(FileStatus.complete).required()\n})\n\nexport const metadataSchema = joi\n .object<FileUploadMetadata>()\n .keys({\n retrievalKey: joi.string().email().required()\n })\n .required()\n\nexport const tempStatusSchema = joi\n .object<UploadStatusFileResponse>({\n uploadStatus: joi\n .string()\n .valid(UploadStatus.ready, UploadStatus.pending)\n .required(),\n metadata: metadataSchema,\n form: joi.object().required().keys({\n file: tempFileSchema\n }),\n numberOfRejectedFiles: joi.number().optional()\n })\n .required()\n\nexport const formStatusSchema = joi\n .object<UploadStatusResponse>({\n uploadStatus: joi.string().valid(UploadStatus.ready).required(),\n metadata: metadataSchema,\n form: joi.object().required().keys({\n file: formFileSchema\n }),\n numberOfRejectedFiles: joi.number().valid(0).required()\n })\n .required()\n\nexport const itemSchema = joi.object<FileState>({\n uploadId: uploadIdSchema\n})\n\nexport const tempItemSchema = itemSchema.append({\n status: tempStatusSchema\n})\n\nexport const formItemSchema = itemSchema.append({\n status: formStatusSchema\n})\n\nexport class FileUploadField extends FormComponent {\n declare options: FileUploadFieldComponent['options']\n declare schema: FileUploadFieldComponent['schema']\n declare formSchema: ArraySchema<FileState>\n declare stateSchema: ArraySchema<FileState>\n\n constructor(\n def: FileUploadFieldComponent,\n props: ConstructorParameters<typeof FormComponent>[1]\n ) {\n super(def, props)\n\n const { options, schema } = def\n\n let formSchema = joi\n .array<FileState>()\n .label(this.label)\n .single()\n .required()\n\n if (options.required === false) {\n formSchema = formSchema.optional()\n }\n\n if (typeof schema.length !== 'number') {\n if (typeof schema.max === 'number') {\n formSchema = formSchema.max(schema.max)\n }\n\n if (typeof schema.min === 'number') {\n formSchema = formSchema.min(schema.min)\n }\n } else {\n formSchema = formSchema.length(schema.length)\n }\n\n this.formSchema = formSchema.items(formItemSchema)\n this.stateSchema = formSchema\n .items(formItemSchema)\n .default(null)\n .allow(null)\n\n this.options = options\n this.schema = schema\n }\n\n getFormValueFromState(state: FormSubmissionState) {\n const { name } = this\n return this.getFormValue(state[name])\n }\n\n getFormValue(value?: FormStateValue | FormState) {\n return this.isValue(value) ? value : undefined\n }\n\n getDisplayStringFromFormValue(files: FileState[] | undefined): string {\n if (!files?.length) {\n return ''\n }\n\n const unit = files.length === 1 ? 'file' : 'files'\n return `Uploaded ${files.length} ${unit}`\n }\n\n getDisplayStringFromState(state: FormSubmissionState) {\n const files = this.getFormValueFromState(state)\n\n return this.getDisplayStringFromFormValue(files)\n }\n\n getContextValueFromFormValue(\n files: UploadState | undefined\n ): string[] | null {\n return files?.map(({ status }) => status.form.file.fileId) ?? null\n }\n\n getContextValueFromState(state: FormSubmissionState) {\n const files = this.getFormValueFromState(state)\n return this.getContextValueFromFormValue(files)\n }\n\n getViewModel(\n payload: FormPayload,\n errors?: FormSubmissionError[],\n query: FormQuery = {}\n ) {\n const { options, page } = this\n\n // Allow preview URL direct access\n const isForceAccess = 'force' in query\n\n const viewModel = super.getViewModel(payload, errors)\n const { attributes, id, value } = viewModel\n\n const files = this.getFormValue(value) ?? []\n const filtered = files.filter(\n (file) => file.status.form.file.fileStatus === FileStatus.complete\n )\n const count = filtered.length\n\n const rows: SummaryListRow[] = filtered.map((item, index) => {\n const { status } = item\n const { form } = status\n const { file } = form\n\n const tag = { classes: 'govuk-tag--green', text: 'Uploaded' }\n\n const valueHtml = render\n .view('components/fileuploadfield-value.html', {\n context: { params: { tag } }\n })\n .trim()\n\n const keyHtml = render\n .view('components/fileuploadfield-key.html', {\n context: {\n params: {\n name: file.filename,\n errorMessage: errors && file.errorMessage\n }\n }\n })\n .trim()\n\n const items: SummaryListAction[] = []\n\n // Remove summary list actions from previews\n if (!isForceAccess) {\n const path = `/${item.uploadId}/confirm-delete`\n const href = page?.getHref(`${page.path}${path}`) ?? '#'\n\n items.push({\n href,\n text: 'Remove',\n classes: 'govuk-link--no-visited-state',\n attributes: { id: `${id}__${index}` },\n visuallyHiddenText: file.filename\n })\n }\n\n return {\n key: {\n html: keyHtml\n },\n value: {\n html: valueHtml\n },\n actions: {\n items\n }\n } satisfies SummaryListRow\n })\n\n // Set up the `accept` attribute\n if ('accept' in options && options.accept) {\n attributes.accept = options.accept\n }\n\n const summaryList: SummaryList = {\n classes: 'govuk-summary-list--long-key',\n rows\n }\n\n return {\n ...viewModel,\n\n // File input can't have a initial value\n value: '',\n\n // Override the component name we send to CDP\n name: 'file',\n\n upload: {\n count,\n summaryList\n }\n }\n }\n\n isValue(value?: FormStateValue | FormState): value is UploadState {\n return isUploadState(value)\n }\n\n /**\n * For error preview page that shows all possible errors on a component\n */\n getAllPossibleErrors(): ErrorMessageTemplateList {\n return FileUploadField.getAllPossibleErrors()\n }\n\n /**\n * Static version of getAllPossibleErrors that doesn't require a component instance.\n */\n static getAllPossibleErrors(): ErrorMessageTemplateList {\n return {\n baseErrors: [\n { type: 'selectRequired', template: messageTemplate.selectRequired },\n {\n type: 'filesMimes',\n template: 'The selected file must be a {{#limit}}'\n },\n {\n type: 'filesSize',\n template: 'The selected file must be smaller than 100MB'\n },\n { type: 'filesEmpty', template: 'The selected file is empty' },\n { type: 'filesVirus', template: 'The selected file contains a virus' },\n {\n type: 'filesPartial',\n template: 'The selected file has not fully uploaded'\n },\n {\n type: 'filesError',\n template: 'The selected file could not be uploaded – try again'\n }\n ],\n advancedSettingsErrors: [\n {\n type: 'filesMin',\n template: 'You must upload {{#limit}} files or more'\n },\n {\n type: 'filesMax',\n template: 'You can only upload {{#limit}} files or less'\n },\n {\n type: 'filesExact',\n template: 'You must upload exactly {{#limit}} files'\n }\n ]\n }\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"FileUploadField.js","names":["Boom","joi","FormComponent","isUploadState","InvalidComponentStateError","messageTemplate","FileStatus","UploadStatus","render","uploadIdSchema","string","uuid","required","fileSchema","object","fileId","filename","contentLength","number","tempFileSchema","append","fileStatus","valid","complete","rejected","pending","errorMessage","optional","formFileSchema","metadataSchema","keys","retrievalKey","email","tempStatusSchema","uploadStatus","ready","metadata","form","file","numberOfRejectedFiles","formStatusSchema","itemSchema","uploadId","tempItemSchema","status","formItemSchema","FileUploadField","constructor","def","props","options","schema","formSchema","array","label","single","length","max","min","items","stateSchema","default","allow","getFormValueFromState","state","name","getFormValue","value","isValue","undefined","getDisplayStringFromFormValue","files","unit","getDisplayStringFromState","getContextValueFromFormValue","map","getContextValueFromState","getViewModel","payload","errors","query","page","isForceAccess","viewModel","attributes","id","filtered","filter","count","rows","item","index","tag","classes","text","valueHtml","view","context","params","trim","keyHtml","path","href","getHref","push","visuallyHiddenText","key","html","actions","accept","summaryList","upload","getAllPossibleErrors","onSubmit","request","notificationEmail","Error","app","model","services","formSubmissionService","values","initiatedRetrievalKey","persistFiles","error","isBoom","output","statusCode","baseErrors","type","template","selectRequired","advancedSettingsErrors"],"sources":["../../../../../src/server/plugins/engine/components/FileUploadField.ts"],"sourcesContent":["import {\n type FileUploadFieldComponent,\n type FormMetadata\n} from '@defra/forms-model'\nimport Boom from '@hapi/boom'\nimport joi, { type ArraySchema } from 'joi'\n\nimport {\n FormComponent,\n isUploadState\n} from '~/src/server/plugins/engine/components/FormComponent.js'\nimport { InvalidComponentStateError } from '~/src/server/plugins/engine/pageControllers/errors.js'\nimport { messageTemplate } from '~/src/server/plugins/engine/pageControllers/validationOptions.js'\nimport {\n FileStatus,\n UploadStatus,\n type ErrorMessageTemplateList,\n type FileState,\n type FileUpload,\n type FileUploadMetadata,\n type FormContext,\n type FormPayload,\n type FormState,\n type FormStateValue,\n type FormSubmissionError,\n type FormSubmissionState,\n type SummaryList,\n type SummaryListAction,\n type SummaryListRow,\n type UploadState,\n type UploadStatusFileResponse,\n type UploadStatusResponse\n} from '~/src/server/plugins/engine/types.js'\nimport { render } from '~/src/server/plugins/nunjucks/index.js'\nimport {\n type FormQuery,\n type FormRequestPayload\n} from '~/src/server/routes/types.js'\n\nexport const uploadIdSchema = joi.string().uuid().required()\n\nexport const fileSchema = joi\n .object<FileUpload>({\n fileId: joi.string().uuid().required(),\n filename: joi.string().required(),\n contentLength: joi.number().required()\n })\n .required()\n\nexport const tempFileSchema = fileSchema.append({\n fileStatus: joi\n .string()\n .valid(FileStatus.complete, FileStatus.rejected, FileStatus.pending)\n .required(),\n errorMessage: joi.string().optional()\n})\n\nexport const formFileSchema = fileSchema.append({\n fileStatus: joi.string().valid(FileStatus.complete).required()\n})\n\nexport const metadataSchema = joi\n .object<FileUploadMetadata>()\n .keys({\n retrievalKey: joi.string().email().required()\n })\n .required()\n\nexport const tempStatusSchema = joi\n .object<UploadStatusFileResponse>({\n uploadStatus: joi\n .string()\n .valid(UploadStatus.ready, UploadStatus.pending)\n .required(),\n metadata: metadataSchema,\n form: joi.object().required().keys({\n file: tempFileSchema\n }),\n numberOfRejectedFiles: joi.number().optional()\n })\n .required()\n\nexport const formStatusSchema = joi\n .object<UploadStatusResponse>({\n uploadStatus: joi.string().valid(UploadStatus.ready).required(),\n metadata: metadataSchema,\n form: joi.object().required().keys({\n file: formFileSchema\n }),\n numberOfRejectedFiles: joi.number().valid(0).required()\n })\n .required()\n\nexport const itemSchema = joi.object<FileState>({\n uploadId: uploadIdSchema\n})\n\nexport const tempItemSchema = itemSchema.append({\n status: tempStatusSchema\n})\n\nexport const formItemSchema = itemSchema.append({\n status: formStatusSchema\n})\n\nexport class FileUploadField extends FormComponent {\n declare options: FileUploadFieldComponent['options']\n declare schema: FileUploadFieldComponent['schema']\n declare formSchema: ArraySchema<FileState>\n declare stateSchema: ArraySchema<FileState>\n\n constructor(\n def: FileUploadFieldComponent,\n props: ConstructorParameters<typeof FormComponent>[1]\n ) {\n super(def, props)\n\n const { options, schema } = def\n\n let formSchema = joi\n .array<FileState>()\n .label(this.label)\n .single()\n .required()\n\n if (options.required === false) {\n formSchema = formSchema.optional()\n }\n\n if (typeof schema.length !== 'number') {\n if (typeof schema.max === 'number') {\n formSchema = formSchema.max(schema.max)\n }\n\n if (typeof schema.min === 'number') {\n formSchema = formSchema.min(schema.min)\n }\n } else {\n formSchema = formSchema.length(schema.length)\n }\n\n this.formSchema = formSchema.items(formItemSchema)\n this.stateSchema = formSchema\n .items(formItemSchema)\n .default(null)\n .allow(null)\n\n this.options = options\n this.schema = schema\n }\n\n getFormValueFromState(state: FormSubmissionState) {\n const { name } = this\n return this.getFormValue(state[name])\n }\n\n getFormValue(value?: FormStateValue | FormState) {\n return this.isValue(value) ? value : undefined\n }\n\n getDisplayStringFromFormValue(files: FileState[] | undefined): string {\n if (!files?.length) {\n return ''\n }\n\n const unit = files.length === 1 ? 'file' : 'files'\n return `Uploaded ${files.length} ${unit}`\n }\n\n getDisplayStringFromState(state: FormSubmissionState) {\n const files = this.getFormValueFromState(state)\n\n return this.getDisplayStringFromFormValue(files)\n }\n\n getContextValueFromFormValue(\n files: UploadState | undefined\n ): string[] | null {\n return files?.map(({ status }) => status.form.file.fileId) ?? null\n }\n\n getContextValueFromState(state: FormSubmissionState) {\n const files = this.getFormValueFromState(state)\n return this.getContextValueFromFormValue(files)\n }\n\n getViewModel(\n payload: FormPayload,\n errors?: FormSubmissionError[],\n query: FormQuery = {}\n ) {\n const { options, page } = this\n\n // Allow preview URL direct access\n const isForceAccess = 'force' in query\n\n const viewModel = super.getViewModel(payload, errors)\n const { attributes, id, value } = viewModel\n\n const files = this.getFormValue(value) ?? []\n const filtered = files.filter(\n (file) => file.status.form.file.fileStatus === FileStatus.complete\n )\n const count = filtered.length\n\n const rows: SummaryListRow[] = filtered.map((item, index) => {\n const { status } = item\n const { form } = status\n const { file } = form\n\n const tag = { classes: 'govuk-tag--green', text: 'Uploaded' }\n\n const valueHtml = render\n .view('components/fileuploadfield-value.html', {\n context: { params: { tag } }\n })\n .trim()\n\n const keyHtml = render\n .view('components/fileuploadfield-key.html', {\n context: {\n params: {\n name: file.filename,\n errorMessage: errors && file.errorMessage\n }\n }\n })\n .trim()\n\n const items: SummaryListAction[] = []\n\n // Remove summary list actions from previews\n if (!isForceAccess) {\n const path = `/${item.uploadId}/confirm-delete`\n const href = page?.getHref(`${page.path}${path}`) ?? '#'\n\n items.push({\n href,\n text: 'Remove',\n classes: 'govuk-link--no-visited-state',\n attributes: { id: `${id}__${index}` },\n visuallyHiddenText: file.filename\n })\n }\n\n return {\n key: {\n html: keyHtml\n },\n value: {\n html: valueHtml\n },\n actions: {\n items\n }\n } satisfies SummaryListRow\n })\n\n // Set up the `accept` attribute\n if ('accept' in options && options.accept) {\n attributes.accept = options.accept\n }\n\n const summaryList: SummaryList = {\n classes: 'govuk-summary-list--long-key',\n rows\n }\n\n return {\n ...viewModel,\n\n // File input can't have a initial value\n value: '',\n\n // Override the component name we send to CDP\n name: 'file',\n\n upload: {\n count,\n summaryList\n }\n }\n }\n\n isValue(value?: FormStateValue | FormState): value is UploadState {\n return isUploadState(value)\n }\n\n /**\n * For error preview page that shows all possible errors on a component\n */\n getAllPossibleErrors(): ErrorMessageTemplateList {\n return FileUploadField.getAllPossibleErrors()\n }\n\n async onSubmit(\n request: FormRequestPayload,\n metadata: FormMetadata,\n context: FormContext\n ) {\n const notificationEmail = metadata.notificationEmail\n\n if (!notificationEmail) {\n // this should not happen because notificationEmail is checked further up\n // the chain in SummaryPageController before submitForm is called.\n throw new Error('Unexpected missing notificationEmail in metadata')\n }\n\n if (!request.app.model?.services.formSubmissionService) {\n throw new Error('No form submission service available in app model')\n }\n\n const { formSubmissionService } = request.app.model.services\n const values = this.getFormValueFromState(context.state) ?? []\n\n const files = values.map((value) => ({\n fileId: value.status.form.file.fileId,\n initiatedRetrievalKey: value.status.metadata.retrievalKey\n }))\n\n if (!files.length) {\n return\n }\n\n try {\n await formSubmissionService.persistFiles(files, notificationEmail)\n } catch (error) {\n if (\n Boom.isBoom(error) &&\n (error.output.statusCode === 403 || // Forbidden - retrieval key invalid\n error.output.statusCode === 410) // Gone - file expired (took to long to submit, etc)\n ) {\n // Failed to persist files. We can't recover from this, the only real way we can recover the submissions is\n // by resetting the problematic components and letting the user re-try.\n // Scenarios: file missing from S3, invalid retrieval key (timing problem), etc.\n throw new InvalidComponentStateError(\n this,\n 'There was a problem with your uploaded files. Re-upload them before submitting the form again.'\n )\n }\n\n throw error\n }\n }\n\n /**\n * Static version of getAllPossibleErrors that doesn't require a component instance.\n */\n static getAllPossibleErrors(): ErrorMessageTemplateList {\n return {\n baseErrors: [\n { type: 'selectRequired', template: messageTemplate.selectRequired },\n {\n type: 'filesMimes',\n template: 'The selected file must be a {{#limit}}'\n },\n {\n type: 'filesSize',\n template: 'The selected file must be smaller than 100MB'\n },\n { type: 'filesEmpty', template: 'The selected file is empty' },\n { type: 'filesVirus', template: 'The selected file contains a virus' },\n {\n type: 'filesPartial',\n template: 'The selected file has not fully uploaded'\n },\n {\n type: 'filesError',\n template: 'The selected file could not be uploaded – try again'\n }\n ],\n advancedSettingsErrors: [\n {\n type: 'filesMin',\n template: 'You must upload {{#limit}} files or more'\n },\n {\n type: 'filesMax',\n template: 'You can only upload {{#limit}} files or less'\n },\n {\n type: 'filesExact',\n template: 'You must upload exactly {{#limit}} files'\n }\n ]\n }\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { type FormComponentsDef, type Item } from '@defra/forms-model';
|
|
1
|
+
import { type FormComponentsDef, type FormMetadata, type Item } from '@defra/forms-model';
|
|
2
2
|
import { ComponentBase } from '~/src/server/plugins/engine/components/ComponentBase.js';
|
|
3
|
+
import { type FormContext, type FormRequestPayload } from '~/src/server/plugins/engine/types/index.js';
|
|
3
4
|
import { type ErrorMessageTemplateList, type FileState, type FormPayload, type FormState, type FormStateValue, type FormSubmissionError, type FormSubmissionState, type FormValue, type RepeatItemState, type RepeatListState, type UploadState } from '~/src/server/plugins/engine/types.js';
|
|
4
5
|
export declare class FormComponent extends ComponentBase {
|
|
5
6
|
type: FormComponentsDef['type'];
|
|
@@ -27,8 +28,8 @@ export declare class FormComponent extends ComponentBase {
|
|
|
27
28
|
id?: string;
|
|
28
29
|
text: string;
|
|
29
30
|
};
|
|
30
|
-
prefix?: import("
|
|
31
|
-
suffix?: import("
|
|
31
|
+
prefix?: import("~/src/server/plugins/engine/types/index.js").ComponentText;
|
|
32
|
+
suffix?: import("~/src/server/plugins/engine/types/index.js").ComponentText;
|
|
32
33
|
classes?: string;
|
|
33
34
|
condition?: string;
|
|
34
35
|
errors?: FormSubmissionError[];
|
|
@@ -44,21 +45,21 @@ export declare class FormComponent extends ComponentBase {
|
|
|
44
45
|
accept?: string;
|
|
45
46
|
inputmode?: string;
|
|
46
47
|
};
|
|
47
|
-
content?: import("
|
|
48
|
+
content?: import("~/src/server/plugins/engine/types/index.js").Content | import("~/src/server/plugins/engine/types/index.js").Content[] | string;
|
|
48
49
|
maxlength?: number;
|
|
49
50
|
maxwords?: number;
|
|
50
51
|
rows?: number;
|
|
51
|
-
items?: import("
|
|
52
|
+
items?: import("~/src/server/plugins/engine/types/index.js").ListItem[] | import("~/src/server/plugins/engine/types/index.js").DateInputItem[];
|
|
52
53
|
fieldset?: {
|
|
53
54
|
attributes?: string | Record<string, string>;
|
|
54
|
-
legend?: import("
|
|
55
|
+
legend?: import("~/src/server/plugins/engine/types/index.js").Label;
|
|
55
56
|
};
|
|
56
57
|
formGroup?: {
|
|
57
58
|
classes?: string;
|
|
58
59
|
attributes?: string | Record<string, string>;
|
|
59
60
|
};
|
|
60
61
|
showFieldsetError?: boolean;
|
|
61
|
-
components?: import("
|
|
62
|
+
components?: import("~/src/server/plugins/engine/types/index.js").ComponentViewModel[];
|
|
62
63
|
upload?: {
|
|
63
64
|
count: number;
|
|
64
65
|
summaryList: import("~/src/server/plugins/engine/types.js").SummaryList;
|
|
@@ -78,6 +79,7 @@ export declare class FormComponent extends ComponentBase {
|
|
|
78
79
|
* Static version of getAllPossibleErrors that doesn't require a component instance.
|
|
79
80
|
*/
|
|
80
81
|
static getAllPossibleErrors(): ErrorMessageTemplateList;
|
|
82
|
+
onSubmit(_request: FormRequestPayload, _metadata: FormMetadata, _context: FormContext): Promise<void>;
|
|
81
83
|
}
|
|
82
84
|
/**
|
|
83
85
|
* Check for form value
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FormComponent.js","names":["ComponentBase","optionalText","FormComponent","type","hint","label","isFormComponent","constructor","def","props","shortDescription","title","keys","collection","name","fields","map","getFormDataFromState","state","getFormValue","getFormValueFromState","value","isValue","undefined","getStateFromValidForm","payload","getErrors","errors","list","filter","error","path","includes","length","getFirstError","getViewErrors","firstError","getViewModel","options","viewModel","isRequired","required","hideOptional","text","componentErrors","componentError","errorMessage","id","getDisplayStringFromFormValue","toString","getDisplayStringFromState","getContextValueFromFormValue","isState","values","Object","isFormValue","Array","isArray","getContextValueFromState","isFormState","getAllPossibleErrors","baseErrors","advancedSettingsErrors","isRepeatState","every","isRepeatValue","itemId","isUploadState","isUploadValue","uploadId"],"sources":["../../../../../src/server/plugins/engine/components/FormComponent.ts"],"sourcesContent":["import { type FormComponentsDef, type Item } from '@defra/forms-model'\n\nimport { ComponentBase } from '~/src/server/plugins/engine/components/ComponentBase.js'\nimport { optionalText } from '~/src/server/plugins/engine/components/constants.js'\nimport {\n type ErrorMessageTemplateList,\n type FileState,\n type FormPayload,\n type FormState,\n type FormStateValue,\n type FormSubmissionError,\n type FormSubmissionState,\n type FormValue,\n type RepeatItemState,\n type RepeatListState,\n type UploadState\n} from '~/src/server/plugins/engine/types.js'\n\nexport class FormComponent extends ComponentBase {\n type: FormComponentsDef['type']\n hint: FormComponentsDef['hint']\n label: string\n\n isFormComponent = true\n\n constructor(\n def: FormComponentsDef,\n props: ConstructorParameters<typeof ComponentBase>[1]\n ) {\n super(def, props)\n\n const { hint, type } = def\n\n this.type = type\n this.hint = hint\n this.label =\n 'shortDescription' in def && def.shortDescription\n ? def.shortDescription\n : def.title\n }\n\n get keys() {\n const { collection, name } = this\n\n if (collection) {\n const { fields } = collection\n return [name, ...fields.map(({ name }) => name)]\n }\n\n return [name]\n }\n\n getFormDataFromState(state: FormSubmissionState): FormPayload {\n const { collection, name } = this\n\n if (collection) {\n return collection.getFormDataFromState(state)\n }\n\n return {\n [name]: this.getFormValue(state[name])\n }\n }\n\n getFormValueFromState(state: FormSubmissionState): FormValue | FormPayload {\n const { collection, name } = this\n\n if (collection) {\n return collection.getFormValueFromState(state)\n }\n\n return this.getFormValue(state[name])\n }\n\n getFormValue(value?: FormStateValue | FormState) {\n return this.isValue(value) ? value : undefined\n }\n\n getStateFromValidForm(payload: FormPayload): FormState {\n const { collection, name } = this\n\n if (collection) {\n return collection.getStateFromValidForm(payload)\n }\n\n return {\n [name]: this.getFormValue(payload[name]) ?? null\n }\n }\n\n getErrors(errors?: FormSubmissionError[]): FormSubmissionError[] | undefined {\n const { name } = this\n\n // Filter component and child errors only\n const list = errors?.filter(\n (error) =>\n error.name === name ||\n error.path.includes(name) ||\n this.keys.includes(error.name)\n )\n\n if (!list?.length) {\n return\n }\n\n return list\n }\n\n getFirstError(\n errors?: FormSubmissionError[]\n ): FormSubmissionError | undefined {\n return this.getErrors(errors)?.[0]\n }\n\n getViewErrors(\n errors?: FormSubmissionError[]\n ): FormSubmissionError[] | undefined {\n const firstError = this.getFirstError(errors)\n return firstError && [firstError]\n }\n\n getViewModel(payload: FormPayload, errors?: FormSubmissionError[]) {\n const { hint, name, options = {}, title, viewModel } = this\n\n const isRequired = !('required' in options) || options.required !== false\n const hideOptional = 'optionalText' in options && options.optionalText\n const label = `${title}${!isRequired && !hideOptional ? optionalText : ''}`\n\n if (hint) {\n viewModel.hint = {\n text: hint\n }\n }\n\n // Filter component errors only\n const componentErrors = this.getErrors(errors)\n const componentError = this.getFirstError(componentErrors)\n\n if (componentErrors) {\n viewModel.errors = componentErrors\n }\n\n if (componentError) {\n viewModel.errorMessage = {\n text: componentError.text\n }\n }\n\n return {\n ...viewModel,\n label: {\n text: label\n },\n id: name,\n name,\n value: payload[name]\n }\n }\n\n getDisplayStringFromFormValue(value: FormValue | FormPayload): string {\n // Map selected values to text\n // eslint-disable-next-line @typescript-eslint/no-base-to-string\n return this.isValue(value) ? value.toString() : ''\n }\n\n getDisplayStringFromState(state: FormSubmissionState): string {\n const value = this.getFormValueFromState(state)\n // eslint-disable-next-line @typescript-eslint/no-base-to-string\n return this.getDisplayStringFromFormValue(value)\n }\n\n getContextValueFromFormValue(\n value: FormValue | FormPayload\n ): Item['value'] | Item['value'][] | null {\n // Filter object field values\n if (this.isState(value)) {\n const values = Object.values(value).filter(isFormValue)\n return values.length ? values : null\n }\n\n // Filter array field values\n if (this.isValue(value) && Array.isArray(value)) {\n return value.filter(isFormValue)\n }\n\n return this.isValue(value) ? value : null\n }\n\n getContextValueFromState(\n state: FormSubmissionState\n ): Item['value'] | Item['value'][] | null {\n const value = this.getFormValueFromState(state)\n\n return this.getContextValueFromFormValue(value)\n }\n\n isValue(\n value?: FormStateValue | FormState\n ): value is NonNullable<FormStateValue> {\n return isFormValue(value)\n }\n\n isState(value?: FormStateValue | FormState): value is FormState {\n return isFormState(value)\n }\n\n /**\n * For error preview page that shows all possible errors on a component\n */\n getAllPossibleErrors(): ErrorMessageTemplateList {\n return FormComponent.getAllPossibleErrors()\n }\n\n /**\n * Static version of getAllPossibleErrors that doesn't require a component instance.\n */\n static getAllPossibleErrors(): ErrorMessageTemplateList {\n return {\n baseErrors: [],\n advancedSettingsErrors: []\n }\n }\n}\n\n/**\n * Check for form value\n */\nexport function isFormValue(\n value?: unknown\n): value is string | number | boolean {\n return (\n (typeof value === 'string' && value.length > 0) ||\n typeof value === 'number' ||\n typeof value === 'boolean'\n )\n}\n\n/**\n * Check for form state with nested values\n */\nexport function isFormState(value?: unknown): value is FormState {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) {\n return false\n }\n\n // Skip empty objects\n return !!Object.values(value).length\n}\n\n/**\n * Check for repeat list state\n */\nexport function isRepeatState(value?: unknown): value is RepeatListState {\n if (!Array.isArray(value)) {\n return false\n }\n\n // Skip checks when empty\n if (!value.length) {\n return true\n }\n\n return value.every(isRepeatValue)\n}\n\n/**\n * Check for repeat list value\n */\nexport function isRepeatValue(value?: unknown): value is RepeatItemState {\n return isFormState(value) && typeof value.itemId === 'string'\n}\n\n/**\n * Check for upload state\n */\nexport function isUploadState(value?: unknown): value is UploadState {\n if (!Array.isArray(value)) {\n return false\n }\n\n // Skip checks when empty\n if (!value.length) {\n return true\n }\n\n return value.every(isUploadValue)\n}\n\n/**\n * Check for upload state value\n */\nexport function isUploadValue(value?: unknown): value is FileState {\n return isFormState(value) && typeof value.uploadId === 'string'\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"FormComponent.js","names":["ComponentBase","optionalText","FormComponent","type","hint","label","isFormComponent","constructor","def","props","shortDescription","title","keys","collection","name","fields","map","getFormDataFromState","state","getFormValue","getFormValueFromState","value","isValue","undefined","getStateFromValidForm","payload","getErrors","errors","list","filter","error","path","includes","length","getFirstError","getViewErrors","firstError","getViewModel","options","viewModel","isRequired","required","hideOptional","text","componentErrors","componentError","errorMessage","id","getDisplayStringFromFormValue","toString","getDisplayStringFromState","getContextValueFromFormValue","isState","values","Object","isFormValue","Array","isArray","getContextValueFromState","isFormState","getAllPossibleErrors","baseErrors","advancedSettingsErrors","onSubmit","_request","_metadata","_context","Promise","resolve","isRepeatState","every","isRepeatValue","itemId","isUploadState","isUploadValue","uploadId"],"sources":["../../../../../src/server/plugins/engine/components/FormComponent.ts"],"sourcesContent":["import {\n type FormComponentsDef,\n type FormMetadata,\n type Item\n} from '@defra/forms-model'\n\nimport { ComponentBase } from '~/src/server/plugins/engine/components/ComponentBase.js'\nimport { optionalText } from '~/src/server/plugins/engine/components/constants.js'\nimport {\n type FormContext,\n type FormRequestPayload\n} from '~/src/server/plugins/engine/types/index.js'\nimport {\n type ErrorMessageTemplateList,\n type FileState,\n type FormPayload,\n type FormState,\n type FormStateValue,\n type FormSubmissionError,\n type FormSubmissionState,\n type FormValue,\n type RepeatItemState,\n type RepeatListState,\n type UploadState\n} from '~/src/server/plugins/engine/types.js'\n\nexport class FormComponent extends ComponentBase {\n type: FormComponentsDef['type']\n hint: FormComponentsDef['hint']\n label: string\n\n isFormComponent = true\n\n constructor(\n def: FormComponentsDef,\n props: ConstructorParameters<typeof ComponentBase>[1]\n ) {\n super(def, props)\n\n const { hint, type } = def\n\n this.type = type\n this.hint = hint\n this.label =\n 'shortDescription' in def && def.shortDescription\n ? def.shortDescription\n : def.title\n }\n\n get keys() {\n const { collection, name } = this\n\n if (collection) {\n const { fields } = collection\n return [name, ...fields.map(({ name }) => name)]\n }\n\n return [name]\n }\n\n getFormDataFromState(state: FormSubmissionState): FormPayload {\n const { collection, name } = this\n\n if (collection) {\n return collection.getFormDataFromState(state)\n }\n\n return {\n [name]: this.getFormValue(state[name])\n }\n }\n\n getFormValueFromState(state: FormSubmissionState): FormValue | FormPayload {\n const { collection, name } = this\n\n if (collection) {\n return collection.getFormValueFromState(state)\n }\n\n return this.getFormValue(state[name])\n }\n\n getFormValue(value?: FormStateValue | FormState) {\n return this.isValue(value) ? value : undefined\n }\n\n getStateFromValidForm(payload: FormPayload): FormState {\n const { collection, name } = this\n\n if (collection) {\n return collection.getStateFromValidForm(payload)\n }\n\n return {\n [name]: this.getFormValue(payload[name]) ?? null\n }\n }\n\n getErrors(errors?: FormSubmissionError[]): FormSubmissionError[] | undefined {\n const { name } = this\n\n // Filter component and child errors only\n const list = errors?.filter(\n (error) =>\n error.name === name ||\n error.path.includes(name) ||\n this.keys.includes(error.name)\n )\n\n if (!list?.length) {\n return\n }\n\n return list\n }\n\n getFirstError(\n errors?: FormSubmissionError[]\n ): FormSubmissionError | undefined {\n return this.getErrors(errors)?.[0]\n }\n\n getViewErrors(\n errors?: FormSubmissionError[]\n ): FormSubmissionError[] | undefined {\n const firstError = this.getFirstError(errors)\n return firstError && [firstError]\n }\n\n getViewModel(payload: FormPayload, errors?: FormSubmissionError[]) {\n const { hint, name, options = {}, title, viewModel } = this\n\n const isRequired = !('required' in options) || options.required !== false\n const hideOptional = 'optionalText' in options && options.optionalText\n const label = `${title}${!isRequired && !hideOptional ? optionalText : ''}`\n\n if (hint) {\n viewModel.hint = {\n text: hint\n }\n }\n\n // Filter component errors only\n const componentErrors = this.getErrors(errors)\n const componentError = this.getFirstError(componentErrors)\n\n if (componentErrors) {\n viewModel.errors = componentErrors\n }\n\n if (componentError) {\n viewModel.errorMessage = {\n text: componentError.text\n }\n }\n\n return {\n ...viewModel,\n label: {\n text: label\n },\n id: name,\n name,\n value: payload[name]\n }\n }\n\n getDisplayStringFromFormValue(value: FormValue | FormPayload): string {\n // Map selected values to text\n // eslint-disable-next-line @typescript-eslint/no-base-to-string\n return this.isValue(value) ? value.toString() : ''\n }\n\n getDisplayStringFromState(state: FormSubmissionState): string {\n const value = this.getFormValueFromState(state)\n // eslint-disable-next-line @typescript-eslint/no-base-to-string\n return this.getDisplayStringFromFormValue(value)\n }\n\n getContextValueFromFormValue(\n value: FormValue | FormPayload\n ): Item['value'] | Item['value'][] | null {\n // Filter object field values\n if (this.isState(value)) {\n const values = Object.values(value).filter(isFormValue)\n return values.length ? values : null\n }\n\n // Filter array field values\n if (this.isValue(value) && Array.isArray(value)) {\n return value.filter(isFormValue)\n }\n\n return this.isValue(value) ? value : null\n }\n\n getContextValueFromState(\n state: FormSubmissionState\n ): Item['value'] | Item['value'][] | null {\n const value = this.getFormValueFromState(state)\n\n return this.getContextValueFromFormValue(value)\n }\n\n isValue(\n value?: FormStateValue | FormState\n ): value is NonNullable<FormStateValue> {\n return isFormValue(value)\n }\n\n isState(value?: FormStateValue | FormState): value is FormState {\n return isFormState(value)\n }\n\n /**\n * For error preview page that shows all possible errors on a component\n */\n getAllPossibleErrors(): ErrorMessageTemplateList {\n return FormComponent.getAllPossibleErrors()\n }\n\n /**\n * Static version of getAllPossibleErrors that doesn't require a component instance.\n */\n static getAllPossibleErrors(): ErrorMessageTemplateList {\n return {\n baseErrors: [],\n advancedSettingsErrors: []\n }\n }\n\n onSubmit(\n _request: FormRequestPayload,\n _metadata: FormMetadata,\n _context: FormContext\n ): Promise<void> {\n return Promise.resolve()\n }\n}\n\n/**\n * Check for form value\n */\nexport function isFormValue(\n value?: unknown\n): value is string | number | boolean {\n return (\n (typeof value === 'string' && value.length > 0) ||\n typeof value === 'number' ||\n typeof value === 'boolean'\n )\n}\n\n/**\n * Check for form state with nested values\n */\nexport function isFormState(value?: unknown): value is FormState {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) {\n return false\n }\n\n // Skip empty objects\n return !!Object.values(value).length\n}\n\n/**\n * Check for repeat list state\n */\nexport function isRepeatState(value?: unknown): value is RepeatListState {\n if (!Array.isArray(value)) {\n return false\n }\n\n // Skip checks when empty\n if (!value.length) {\n return true\n }\n\n return value.every(isRepeatValue)\n}\n\n/**\n * Check for repeat list value\n */\nexport function isRepeatValue(value?: unknown): value is RepeatItemState {\n return isFormState(value) && typeof value.itemId === 'string'\n}\n\n/**\n * Check for upload state\n */\nexport function isUploadState(value?: unknown): value is UploadState {\n if (!Array.isArray(value)) {\n return false\n }\n\n // Skip checks when empty\n if (!value.length) {\n return true\n }\n\n return value.every(isUploadValue)\n}\n\n/**\n * Check for upload state value\n */\nexport function isUploadValue(value?: unknown): value is FileState {\n return isFormState(value) && typeof value.uploadId === 'string'\n}\n"],"mappings":"AAMA,SAASA,aAAa;AACtB,SAASC,YAAY;AAmBrB,OAAO,MAAMC,aAAa,SAASF,aAAa,CAAC;EAC/CG,IAAI;EACJC,IAAI;EACJC,KAAK;EAELC,eAAe,GAAG,IAAI;EAEtBC,WAAWA,CACTC,GAAsB,EACtBC,KAAqD,EACrD;IACA,KAAK,CAACD,GAAG,EAAEC,KAAK,CAAC;IAEjB,MAAM;MAAEL,IAAI;MAAED;IAAK,CAAC,GAAGK,GAAG;IAE1B,IAAI,CAACL,IAAI,GAAGA,IAAI;IAChB,IAAI,CAACC,IAAI,GAAGA,IAAI;IAChB,IAAI,CAACC,KAAK,GACR,kBAAkB,IAAIG,GAAG,IAAIA,GAAG,CAACE,gBAAgB,GAC7CF,GAAG,CAACE,gBAAgB,GACpBF,GAAG,CAACG,KAAK;EACjB;EAEA,IAAIC,IAAIA,CAAA,EAAG;IACT,MAAM;MAAEC,UAAU;MAAEC;IAAK,CAAC,GAAG,IAAI;IAEjC,IAAID,UAAU,EAAE;MACd,MAAM;QAAEE;MAAO,CAAC,GAAGF,UAAU;MAC7B,OAAO,CAACC,IAAI,EAAE,GAAGC,MAAM,CAACC,GAAG,CAAC,CAAC;QAAEF;MAAK,CAAC,KAAKA,IAAI,CAAC,CAAC;IAClD;IAEA,OAAO,CAACA,IAAI,CAAC;EACf;EAEAG,oBAAoBA,CAACC,KAA0B,EAAe;IAC5D,MAAM;MAAEL,UAAU;MAAEC;IAAK,CAAC,GAAG,IAAI;IAEjC,IAAID,UAAU,EAAE;MACd,OAAOA,UAAU,CAACI,oBAAoB,CAACC,KAAK,CAAC;IAC/C;IAEA,OAAO;MACL,CAACJ,IAAI,GAAG,IAAI,CAACK,YAAY,CAACD,KAAK,CAACJ,IAAI,CAAC;IACvC,CAAC;EACH;EAEAM,qBAAqBA,CAACF,KAA0B,EAA2B;IACzE,MAAM;MAAEL,UAAU;MAAEC;IAAK,CAAC,GAAG,IAAI;IAEjC,IAAID,UAAU,EAAE;MACd,OAAOA,UAAU,CAACO,qBAAqB,CAACF,KAAK,CAAC;IAChD;IAEA,OAAO,IAAI,CAACC,YAAY,CAACD,KAAK,CAACJ,IAAI,CAAC,CAAC;EACvC;EAEAK,YAAYA,CAACE,KAAkC,EAAE;IAC/C,OAAO,IAAI,CAACC,OAAO,CAACD,KAAK,CAAC,GAAGA,KAAK,GAAGE,SAAS;EAChD;EAEAC,qBAAqBA,CAACC,OAAoB,EAAa;IACrD,MAAM;MAAEZ,UAAU;MAAEC;IAAK,CAAC,GAAG,IAAI;IAEjC,IAAID,UAAU,EAAE;MACd,OAAOA,UAAU,CAACW,qBAAqB,CAACC,OAAO,CAAC;IAClD;IAEA,OAAO;MACL,CAACX,IAAI,GAAG,IAAI,CAACK,YAAY,CAACM,OAAO,CAACX,IAAI,CAAC,CAAC,IAAI;IAC9C,CAAC;EACH;EAEAY,SAASA,CAACC,MAA8B,EAAqC;IAC3E,MAAM;MAAEb;IAAK,CAAC,GAAG,IAAI;;IAErB;IACA,MAAMc,IAAI,GAAGD,MAAM,EAAEE,MAAM,CACxBC,KAAK,IACJA,KAAK,CAAChB,IAAI,KAAKA,IAAI,IACnBgB,KAAK,CAACC,IAAI,CAACC,QAAQ,CAAClB,IAAI,CAAC,IACzB,IAAI,CAACF,IAAI,CAACoB,QAAQ,CAACF,KAAK,CAAChB,IAAI,CACjC,CAAC;IAED,IAAI,CAACc,IAAI,EAAEK,MAAM,EAAE;MACjB;IACF;IAEA,OAAOL,IAAI;EACb;EAEAM,aAAaA,CACXP,MAA8B,EACG;IACjC,OAAO,IAAI,CAACD,SAAS,CAACC,MAAM,CAAC,GAAG,CAAC,CAAC;EACpC;EAEAQ,aAAaA,CACXR,MAA8B,EACK;IACnC,MAAMS,UAAU,GAAG,IAAI,CAACF,aAAa,CAACP,MAAM,CAAC;IAC7C,OAAOS,UAAU,IAAI,CAACA,UAAU,CAAC;EACnC;EAEAC,YAAYA,CAACZ,OAAoB,EAAEE,MAA8B,EAAE;IACjE,MAAM;MAAEvB,IAAI;MAAEU,IAAI;MAAEwB,OAAO,GAAG,CAAC,CAAC;MAAE3B,KAAK;MAAE4B;IAAU,CAAC,GAAG,IAAI;IAE3D,MAAMC,UAAU,GAAG,EAAE,UAAU,IAAIF,OAAO,CAAC,IAAIA,OAAO,CAACG,QAAQ,KAAK,KAAK;IACzE,MAAMC,YAAY,GAAG,cAAc,IAAIJ,OAAO,IAAIA,OAAO,CAACrC,YAAY;IACtE,MAAMI,KAAK,GAAG,GAAGM,KAAK,GAAG,CAAC6B,UAAU,IAAI,CAACE,YAAY,GAAGzC,YAAY,GAAG,EAAE,EAAE;IAE3E,IAAIG,IAAI,EAAE;MACRmC,SAAS,CAACnC,IAAI,GAAG;QACfuC,IAAI,EAAEvC;MACR,CAAC;IACH;;IAEA;IACA,MAAMwC,eAAe,GAAG,IAAI,CAAClB,SAAS,CAACC,MAAM,CAAC;IAC9C,MAAMkB,cAAc,GAAG,IAAI,CAACX,aAAa,CAACU,eAAe,CAAC;IAE1D,IAAIA,eAAe,EAAE;MACnBL,SAAS,CAACZ,MAAM,GAAGiB,eAAe;IACpC;IAEA,IAAIC,cAAc,EAAE;MAClBN,SAAS,CAACO,YAAY,GAAG;QACvBH,IAAI,EAAEE,cAAc,CAACF;MACvB,CAAC;IACH;IAEA,OAAO;MACL,GAAGJ,SAAS;MACZlC,KAAK,EAAE;QACLsC,IAAI,EAAEtC;MACR,CAAC;MACD0C,EAAE,EAAEjC,IAAI;MACRA,IAAI;MACJO,KAAK,EAAEI,OAAO,CAACX,IAAI;IACrB,CAAC;EACH;EAEAkC,6BAA6BA,CAAC3B,KAA8B,EAAU;IACpE;IACA;IACA,OAAO,IAAI,CAACC,OAAO,CAACD,KAAK,CAAC,GAAGA,KAAK,CAAC4B,QAAQ,CAAC,CAAC,GAAG,EAAE;EACpD;EAEAC,yBAAyBA,CAAChC,KAA0B,EAAU;IAC5D,MAAMG,KAAK,GAAG,IAAI,CAACD,qBAAqB,CAACF,KAAK,CAAC;IAC/C;IACA,OAAO,IAAI,CAAC8B,6BAA6B,CAAC3B,KAAK,CAAC;EAClD;EAEA8B,4BAA4BA,CAC1B9B,KAA8B,EACU;IACxC;IACA,IAAI,IAAI,CAAC+B,OAAO,CAAC/B,KAAK,CAAC,EAAE;MACvB,MAAMgC,MAAM,GAAGC,MAAM,CAACD,MAAM,CAAChC,KAAK,CAAC,CAACQ,MAAM,CAAC0B,WAAW,CAAC;MACvD,OAAOF,MAAM,CAACpB,MAAM,GAAGoB,MAAM,GAAG,IAAI;IACtC;;IAEA;IACA,IAAI,IAAI,CAAC/B,OAAO,CAACD,KAAK,CAAC,IAAImC,KAAK,CAACC,OAAO,CAACpC,KAAK,CAAC,EAAE;MAC/C,OAAOA,KAAK,CAACQ,MAAM,CAAC0B,WAAW,CAAC;IAClC;IAEA,OAAO,IAAI,CAACjC,OAAO,CAACD,KAAK,CAAC,GAAGA,KAAK,GAAG,IAAI;EAC3C;EAEAqC,wBAAwBA,CACtBxC,KAA0B,EACc;IACxC,MAAMG,KAAK,GAAG,IAAI,CAACD,qBAAqB,CAACF,KAAK,CAAC;IAE/C,OAAO,IAAI,CAACiC,4BAA4B,CAAC9B,KAAK,CAAC;EACjD;EAEAC,OAAOA,CACLD,KAAkC,EACI;IACtC,OAAOkC,WAAW,CAAClC,KAAK,CAAC;EAC3B;EAEA+B,OAAOA,CAAC/B,KAAkC,EAAsB;IAC9D,OAAOsC,WAAW,CAACtC,KAAK,CAAC;EAC3B;;EAEA;AACF;AACA;EACEuC,oBAAoBA,CAAA,EAA6B;IAC/C,OAAO1D,aAAa,CAAC0D,oBAAoB,CAAC,CAAC;EAC7C;;EAEA;AACF;AACA;EACE,OAAOA,oBAAoBA,CAAA,EAA6B;IACtD,OAAO;MACLC,UAAU,EAAE,EAAE;MACdC,sBAAsB,EAAE;IAC1B,CAAC;EACH;EAEAC,QAAQA,CACNC,QAA4B,EAC5BC,SAAuB,EACvBC,QAAqB,EACN;IACf,OAAOC,OAAO,CAACC,OAAO,CAAC,CAAC;EAC1B;AACF;;AAEA;AACA;AACA;AACA,OAAO,SAASb,WAAWA,CACzBlC,KAAe,EACqB;EACpC,OACG,OAAOA,KAAK,KAAK,QAAQ,IAAIA,KAAK,CAACY,MAAM,GAAG,CAAC,IAC9C,OAAOZ,KAAK,KAAK,QAAQ,IACzB,OAAOA,KAAK,KAAK,SAAS;AAE9B;;AAEA;AACA;AACA;AACA,OAAO,SAASsC,WAAWA,CAACtC,KAAe,EAAsB;EAC/D,IAAIA,KAAK,KAAK,IAAI,IAAI,OAAOA,KAAK,KAAK,QAAQ,IAAImC,KAAK,CAACC,OAAO,CAACpC,KAAK,CAAC,EAAE;IACvE,OAAO,KAAK;EACd;;EAEA;EACA,OAAO,CAAC,CAACiC,MAAM,CAACD,MAAM,CAAChC,KAAK,CAAC,CAACY,MAAM;AACtC;;AAEA;AACA;AACA;AACA,OAAO,SAASoC,aAAaA,CAAChD,KAAe,EAA4B;EACvE,IAAI,CAACmC,KAAK,CAACC,OAAO,CAACpC,KAAK,CAAC,EAAE;IACzB,OAAO,KAAK;EACd;;EAEA;EACA,IAAI,CAACA,KAAK,CAACY,MAAM,EAAE;IACjB,OAAO,IAAI;EACb;EAEA,OAAOZ,KAAK,CAACiD,KAAK,CAACC,aAAa,CAAC;AACnC;;AAEA;AACA;AACA;AACA,OAAO,SAASA,aAAaA,CAAClD,KAAe,EAA4B;EACvE,OAAOsC,WAAW,CAACtC,KAAK,CAAC,IAAI,OAAOA,KAAK,CAACmD,MAAM,KAAK,QAAQ;AAC/D;;AAEA;AACA;AACA;AACA,OAAO,SAASC,aAAaA,CAACpD,KAAe,EAAwB;EACnE,IAAI,CAACmC,KAAK,CAACC,OAAO,CAACpC,KAAK,CAAC,EAAE;IACzB,OAAO,KAAK;EACd;;EAEA;EACA,IAAI,CAACA,KAAK,CAACY,MAAM,EAAE;IACjB,OAAO,IAAI;EACb;EAEA,OAAOZ,KAAK,CAACiD,KAAK,CAACI,aAAa,CAAC;AACnC;;AAEA;AACA;AACA;AACA,OAAO,SAASA,aAAaA,CAACrD,KAAe,EAAsB;EACjE,OAAOsC,WAAW,CAACtC,KAAK,CAAC,IAAI,OAAOA,KAAK,CAACsD,QAAQ,KAAK,QAAQ;AACjE","ignoreList":[]}
|
|
@@ -45,6 +45,11 @@ export declare function checkEmailAddressForLiveFormSubmission(emailAddress: str
|
|
|
45
45
|
*/
|
|
46
46
|
export declare function getErrors(details?: ValidationErrorItem[]): FormSubmissionError[] | undefined;
|
|
47
47
|
export declare function getError(detail: ValidationErrorItem): FormSubmissionError;
|
|
48
|
+
export declare function createError(componentName: string, message: string): {
|
|
49
|
+
href: string;
|
|
50
|
+
name: string;
|
|
51
|
+
text: string;
|
|
52
|
+
};
|
|
48
53
|
/**
|
|
49
54
|
* A small helper to safely generate a crumb token.
|
|
50
55
|
* Checks that the crumb plugin is available, that crumb
|
|
@@ -208,6 +208,13 @@ export function getError(detail) {
|
|
|
208
208
|
context
|
|
209
209
|
};
|
|
210
210
|
}
|
|
211
|
+
export function createError(componentName, message) {
|
|
212
|
+
return {
|
|
213
|
+
href: `#${componentName}`,
|
|
214
|
+
name: componentName,
|
|
215
|
+
text: message
|
|
216
|
+
};
|
|
217
|
+
}
|
|
211
218
|
|
|
212
219
|
/**
|
|
213
220
|
* A small helper to safely generate a crumb token.
|