@defra/forms-engine-plugin 0.1.10 → 0.1.12
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/javascripts/file-upload.min.js +1 -1
- package/.public/javascripts/file-upload.min.js.map +1 -1
- package/.public/stylesheets/application.min.css +1 -1
- package/.public/stylesheets/application.min.css.map +1 -1
- package/.server/client/javascripts/file-upload.js +45 -4
- package/.server/client/javascripts/file-upload.js.map +1 -1
- package/.server/client/stylesheets/application.scss +10 -0
- package/.server/config/index.js +3 -14
- package/.server/config/index.js.map +1 -1
- package/.server/server/constants.js +2 -0
- package/.server/server/constants.js.map +1 -1
- package/.server/server/devserver/dxt-devtool-baselayout.html +71 -0
- package/.server/server/forms/register-as-a-unicorn-breeder.json +393 -0
- package/.server/server/forms/register-as-a-unicorn-breeder.yaml +251 -0
- package/.server/server/index.js +12 -17
- package/.server/server/index.js.map +1 -1
- package/.server/server/plugins/engine/components/AutocompleteField.js +2 -0
- package/.server/server/plugins/engine/components/AutocompleteField.js.map +1 -1
- package/.server/server/plugins/engine/components/CheckboxesField.js +3 -4
- package/.server/server/plugins/engine/components/CheckboxesField.js.map +1 -1
- package/.server/server/plugins/engine/components/ComponentCollection.js +37 -16
- package/.server/server/plugins/engine/components/ComponentCollection.js.map +1 -1
- package/.server/server/plugins/engine/components/DatePartsField.js +36 -2
- package/.server/server/plugins/engine/components/DatePartsField.js.map +1 -1
- package/.server/server/plugins/engine/components/EmailAddressField.js +19 -3
- package/.server/server/plugins/engine/components/EmailAddressField.js.map +1 -1
- package/.server/server/plugins/engine/components/FileUploadField.js +44 -4
- package/.server/server/plugins/engine/components/FileUploadField.js.map +1 -1
- package/.server/server/plugins/engine/components/FormComponent.js +14 -2
- package/.server/server/plugins/engine/components/FormComponent.js.map +1 -1
- package/.server/server/plugins/engine/components/ListFormComponent.js +16 -3
- package/.server/server/plugins/engine/components/ListFormComponent.js.map +1 -1
- package/.server/server/plugins/engine/components/Markdown.js +24 -0
- package/.server/server/plugins/engine/components/Markdown.js.map +1 -0
- package/.server/server/plugins/engine/components/MonthYearField.js +30 -2
- package/.server/server/plugins/engine/components/MonthYearField.js.map +1 -1
- package/.server/server/plugins/engine/components/MultilineTextField.js +32 -3
- package/.server/server/plugins/engine/components/MultilineTextField.js.map +1 -1
- package/.server/server/plugins/engine/components/NumberField.js +28 -3
- package/.server/server/plugins/engine/components/NumberField.js.map +1 -1
- package/.server/server/plugins/engine/components/SelectionControlField.js +14 -0
- package/.server/server/plugins/engine/components/SelectionControlField.js.map +1 -1
- package/.server/server/plugins/engine/components/TelephoneNumberField.js +19 -3
- package/.server/server/plugins/engine/components/TelephoneNumberField.js.map +1 -1
- package/.server/server/plugins/engine/components/TextField.js +22 -3
- package/.server/server/plugins/engine/components/TextField.js.map +1 -1
- package/.server/server/plugins/engine/components/UkAddressField.js +29 -0
- package/.server/server/plugins/engine/components/UkAddressField.js.map +1 -1
- package/.server/server/plugins/engine/components/YesNoField.js +18 -0
- package/.server/server/plugins/engine/components/YesNoField.js.map +1 -1
- package/.server/server/plugins/engine/components/helpers.js +16 -0
- package/.server/server/plugins/engine/components/helpers.js.map +1 -1
- package/.server/server/plugins/engine/components/index.js +1 -0
- package/.server/server/plugins/engine/components/index.js.map +1 -1
- package/.server/server/plugins/engine/configureEnginePlugin.js +19 -3
- package/.server/server/plugins/engine/configureEnginePlugin.js.map +1 -1
- package/.server/server/plugins/engine/helpers.js +38 -18
- package/.server/server/plugins/engine/helpers.js.map +1 -1
- package/.server/server/plugins/engine/models/FormModel.js +60 -2
- package/.server/server/plugins/engine/models/FormModel.js.map +1 -1
- package/.server/server/plugins/engine/models/SummaryViewModel.js +3 -2
- package/.server/server/plugins/engine/models/SummaryViewModel.js.map +1 -1
- package/.server/server/plugins/engine/outputFormatters/human/v1.js +1 -1
- package/.server/server/plugins/engine/outputFormatters/human/v1.js.map +1 -1
- package/.server/server/plugins/engine/pageControllers/PageController.js +13 -5
- package/.server/server/plugins/engine/pageControllers/PageController.js.map +1 -1
- package/.server/server/plugins/engine/pageControllers/QuestionPageController.js +2 -2
- package/.server/server/plugins/engine/pageControllers/QuestionPageController.js.map +1 -1
- package/.server/server/plugins/engine/pageControllers/SummaryPageController.js +19 -5
- package/.server/server/plugins/engine/pageControllers/SummaryPageController.js.map +1 -1
- package/.server/server/plugins/engine/pageControllers/validationOptions.js +6 -11
- package/.server/server/plugins/engine/pageControllers/validationOptions.js.map +1 -1
- package/.server/server/plugins/engine/plugin.js +32 -20
- package/.server/server/plugins/engine/plugin.js.map +1 -1
- package/.server/server/plugins/engine/services/formsService.js +15 -29
- package/.server/server/plugins/engine/services/formsService.js.map +1 -1
- package/.server/server/plugins/engine/services/localFormsService.js +52 -0
- package/.server/server/plugins/engine/services/localFormsService.js.map +1 -0
- package/.server/server/plugins/engine/services/notifyService.js +1 -4
- package/.server/server/plugins/engine/services/notifyService.js.map +1 -1
- package/.server/server/plugins/engine/services/uploadService.js +5 -3
- package/.server/server/plugins/engine/services/uploadService.js.map +1 -1
- package/.server/server/plugins/engine/types.js.map +1 -1
- package/.server/server/plugins/engine/views/components/html.html +1 -1
- package/.server/server/plugins/engine/views/components/markdown.html +5 -0
- package/.server/server/plugins/engine/views/confirmation.html +1 -1
- package/.server/server/plugins/engine/views/file-upload.html +1 -1
- package/.server/server/plugins/engine/views/index.html +1 -1
- package/.server/server/plugins/engine/views/item-delete.html +1 -1
- package/.server/server/plugins/engine/views/repeat-list-summary.html +1 -1
- package/.server/server/plugins/engine/views/summary.html +8 -2
- package/.server/server/plugins/errorPages.js +4 -26
- package/.server/server/plugins/errorPages.js.map +1 -1
- package/.server/server/plugins/nunjucks/context.js +43 -33
- package/.server/server/plugins/nunjucks/context.js.map +1 -1
- package/.server/server/plugins/nunjucks/context.test.js +23 -28
- package/.server/server/plugins/nunjucks/context.test.js.map +1 -1
- package/.server/server/plugins/nunjucks/enviroment.test.js +6 -3
- package/.server/server/plugins/nunjucks/enviroment.test.js.map +1 -1
- package/.server/server/plugins/nunjucks/types.js +3 -4
- package/.server/server/plugins/nunjucks/types.js.map +1 -1
- package/.server/server/routes/index.js +0 -1
- package/.server/server/routes/index.js.map +1 -1
- package/.server/server/utils/type-utils.js +8 -0
- package/.server/server/utils/type-utils.js.map +1 -0
- package/.server/typings/hapi/index.d.js.map +1 -1
- package/.server/typings/joi/index.d.js.map +1 -1
- package/package.json +4 -3
- package/src/client/javascripts/file-upload.js +60 -4
- package/src/client/stylesheets/application.scss +10 -0
- package/src/config/index.ts +4 -17
- package/src/server/constants.js +2 -0
- package/src/server/devserver/dxt-devtool-baselayout.html +71 -0
- package/src/server/forms/register-as-a-unicorn-breeder.json +393 -0
- package/src/server/forms/register-as-a-unicorn-breeder.yaml +251 -0
- package/src/server/index.test.ts +38 -66
- package/src/server/index.ts +15 -17
- package/src/server/plugins/engine/components/AutocompleteField.test.ts +71 -3
- package/src/server/plugins/engine/components/AutocompleteField.ts +6 -2
- package/src/server/plugins/engine/components/CheckboxesField.test.ts +40 -8
- package/src/server/plugins/engine/components/CheckboxesField.ts +7 -3
- package/src/server/plugins/engine/components/ComponentCollection.ts +45 -18
- package/src/server/plugins/engine/components/DatePartsField.test.ts +13 -4
- package/src/server/plugins/engine/components/DatePartsField.ts +29 -8
- package/src/server/plugins/engine/components/EmailAddressField.test.ts +51 -1
- package/src/server/plugins/engine/components/EmailAddressField.ts +17 -2
- package/src/server/plugins/engine/components/FileUploadField.test.ts +53 -0
- package/src/server/plugins/engine/components/FileUploadField.ts +52 -3
- package/src/server/plugins/engine/components/FormComponent.ts +24 -2
- package/src/server/plugins/engine/components/ListFormComponent.ts +16 -2
- package/src/server/plugins/engine/components/Markdown.test.ts +48 -0
- package/src/server/plugins/engine/components/Markdown.ts +29 -0
- package/src/server/plugins/engine/components/MonthYearField.test.ts +35 -0
- package/src/server/plugins/engine/components/MonthYearField.ts +34 -9
- package/src/server/plugins/engine/components/MultilineTextField.test.ts +83 -5
- package/src/server/plugins/engine/components/MultilineTextField.ts +37 -2
- package/src/server/plugins/engine/components/NumberField.test.ts +24 -2
- package/src/server/plugins/engine/components/NumberField.ts +23 -3
- package/src/server/plugins/engine/components/RadiosField.test.ts +10 -1
- package/src/server/plugins/engine/components/SelectField.test.ts +2 -1
- package/src/server/plugins/engine/components/SelectionControlField.ts +14 -0
- package/src/server/plugins/engine/components/TelephoneNumberField.test.ts +30 -2
- package/src/server/plugins/engine/components/TelephoneNumberField.ts +17 -2
- package/src/server/plugins/engine/components/TextField.test.ts +33 -1
- package/src/server/plugins/engine/components/TextField.ts +17 -2
- package/src/server/plugins/engine/components/UkAddressField.test.ts +46 -3
- package/src/server/plugins/engine/components/UkAddressField.ts +28 -0
- package/src/server/plugins/engine/components/YesNoField.test.ts +9 -1
- package/src/server/plugins/engine/components/YesNoField.ts +24 -0
- package/src/server/plugins/engine/components/helpers.test.ts +24 -0
- package/src/server/plugins/engine/components/helpers.ts +39 -0
- package/src/server/plugins/engine/components/index.ts +1 -0
- package/src/server/plugins/engine/configureEnginePlugin.ts +32 -4
- package/src/server/plugins/engine/helpers.test.ts +71 -20
- package/src/server/plugins/engine/helpers.ts +46 -19
- package/src/server/plugins/engine/models/FormModel.test.ts +91 -1
- package/src/server/plugins/engine/models/FormModel.ts +86 -3
- package/src/server/plugins/engine/models/SummaryViewModel.test.ts +46 -7
- package/src/server/plugins/engine/models/SummaryViewModel.ts +7 -3
- package/src/server/plugins/engine/outputFormatters/human/v1.test.ts +1 -2
- package/src/server/plugins/engine/outputFormatters/human/v1.ts +1 -1
- package/src/server/plugins/engine/pageControllers/FileUploadPageController.test.ts +1 -0
- package/src/server/plugins/engine/pageControllers/PageController.test.ts +9 -6
- package/src/server/plugins/engine/pageControllers/PageController.ts +15 -5
- package/src/server/plugins/engine/pageControllers/QuestionPageController.ts +2 -2
- package/src/server/plugins/engine/pageControllers/SummaryPageController.ts +21 -6
- package/src/server/plugins/engine/pageControllers/validationOptions.ts +31 -17
- package/src/server/plugins/engine/plugin.ts +52 -22
- package/src/server/plugins/engine/services/formsService.js +17 -35
- package/src/server/plugins/engine/services/localFormsService.js +49 -0
- package/src/server/plugins/engine/services/notifyService.ts +1 -2
- package/src/server/plugins/engine/services/uploadService.js +10 -6
- package/src/server/plugins/engine/types.ts +10 -1
- package/src/server/plugins/engine/views/components/html.html +1 -1
- package/src/server/plugins/engine/views/components/markdown.html +5 -0
- package/src/server/plugins/engine/views/confirmation.html +1 -1
- package/src/server/plugins/engine/views/file-upload.html +1 -1
- package/src/server/plugins/engine/views/index.html +1 -1
- package/src/server/plugins/engine/views/item-delete.html +1 -1
- package/src/server/plugins/engine/views/repeat-list-summary.html +1 -1
- package/src/server/plugins/engine/views/summary.html +8 -2
- package/src/server/plugins/errorPages.ts +4 -26
- package/src/server/plugins/nunjucks/context.js +44 -34
- package/src/server/plugins/nunjucks/context.test.js +24 -27
- package/src/server/plugins/nunjucks/enviroment.test.js +9 -3
- package/src/server/plugins/nunjucks/types.js +3 -4
- package/src/server/routes/index.ts +0 -1
- package/src/server/utils/type-utils.ts +15 -0
- package/src/typings/hapi/index.d.ts +3 -9
- package/src/typings/joi/index.d.ts +8 -0
- package/.server/common/cookies.js +0 -55
- package/.server/common/cookies.js.map +0 -1
- package/.server/common/cookies.test.js +0 -15
- package/.server/common/cookies.test.js.map +0 -1
- package/.server/common/types.js +0 -6
- package/.server/common/types.js.map +0 -1
- package/.server/server/forms/README.md +0 -10
- package/.server/server/forms/report-a-terrorist.json +0 -270
- package/.server/server/forms/runner-components-test.json +0 -365
- package/.server/server/forms/test.json +0 -581
- package/.server/server/forms/test.yaml +0 -363
- package/.server/server/plugins/blankie.js +0 -29
- package/.server/server/plugins/blankie.js.map +0 -1
- package/.server/server/plugins/engine/services/formsService.test.js +0 -71
- package/.server/server/plugins/engine/services/formsService.test.js.map +0 -1
- package/.server/server/plugins/engine/views/layout.html +0 -199
- package/.server/server/plugins/router.js +0 -169
- package/.server/server/plugins/router.js.map +0 -1
- package/.server/server/routes/health.js +0 -15
- package/.server/server/routes/health.js.map +0 -1
- package/.server/server/routes/health.test.js +0 -32
- package/.server/server/routes/health.test.js.map +0 -1
- package/.server/server/utils/file-form-service.test.js +0 -52
- package/.server/server/utils/file-form-service.test.js.map +0 -1
- package/.server/server/views/404.html +0 -16
- package/.server/server/views/500.html +0 -19
- package/.server/server/views/help/accessibility-statement.html +0 -58
- package/.server/server/views/help/cookie-preferences.html +0 -57
- package/.server/server/views/help/cookies.html +0 -71
- package/.server/server/views/help/get-support.html +0 -37
- package/.server/server/views/help/privacy-notice.html +0 -68
- package/.server/server/views/help/terms-and-conditions.html +0 -83
- package/src/common/cookies.js +0 -58
- package/src/common/cookies.test.js +0 -23
- package/src/common/types.js +0 -5
- package/src/server/forms/README.md +0 -10
- package/src/server/forms/report-a-terrorist.json +0 -270
- package/src/server/forms/runner-components-test.json +0 -365
- package/src/server/forms/test.json +0 -581
- package/src/server/forms/test.yaml +0 -363
- package/src/server/plugins/blankie.test.ts +0 -73
- package/src/server/plugins/blankie.ts +0 -48
- package/src/server/plugins/engine/services/formsService.test.js +0 -90
- package/src/server/plugins/engine/views/layout.html +0 -199
- package/src/server/plugins/router.ts +0 -201
- package/src/server/routes/health.js +0 -13
- package/src/server/routes/health.test.js +0 -35
- package/src/server/routes/index.test.ts +0 -125
- package/src/server/utils/file-form-service.test.js +0 -79
- package/src/server/views/404.html +0 -16
- package/src/server/views/500.html +0 -19
- package/src/server/views/help/accessibility-statement.html +0 -58
- package/src/server/views/help/cookie-preferences.html +0 -57
- package/src/server/views/help/cookies.html +0 -71
- package/src/server/views/help/get-support.html +0 -37
- package/src/server/views/help/privacy-notice.html +0 -68
- package/src/server/views/help/terms-and-conditions.html +0 -83
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["Engine","CatboxMemory","CatboxRedis","hapi","inert","Scooter","Wreck","blipp","ProxyAgent","config","requestLogger","requestTracing","buildRedisClient","
|
|
1
|
+
{"version":3,"file":"index.js","names":["Engine","CatboxMemory","CatboxRedis","hapi","inert","Scooter","Wreck","blipp","ProxyAgent","config","requestLogger","requestTracing","buildRedisClient","configureCrumbPlugin","configureEnginePlugin","pluginErrorPages","plugin","pluginViews","pluginPulse","pluginSession","publicRoutes","prepareSecureContext","proxyAgent","agents","https","http","httpsAllowUnauthorized","serverOptions","debug","request","get","port","router","stripTrailingSlash","routes","validate","options","abortEarly","security","hsts","maxAge","includeSubDomains","preload","xss","noSniff","xframe","cache","name","engine","client","createServer","routeConfig","server","register","pluginCrumb","pluginEngine","ext","h","response","continue","header","path","startsWith","route"],"sources":["../../src/server/index.ts"],"sourcesContent":["import { Engine as CatboxMemory } from '@hapi/catbox-memory'\nimport { Engine as CatboxRedis } from '@hapi/catbox-redis'\nimport hapi, {\n type Request,\n type ResponseToolkit,\n type ServerOptions\n} from '@hapi/hapi'\nimport inert from '@hapi/inert'\nimport Scooter from '@hapi/scooter'\nimport Wreck from '@hapi/wreck'\nimport blipp from 'blipp'\nimport { ProxyAgent } from 'proxy-agent'\n\nimport { config } from '~/src/config/index.js'\nimport { requestLogger } from '~/src/server/common/helpers/logging/request-logger.js'\nimport { requestTracing } from '~/src/server/common/helpers/logging/request-tracing.js'\nimport { buildRedisClient } from '~/src/server/common/helpers/redis-client.js'\nimport { configureCrumbPlugin } from '~/src/server/plugins/crumb.js'\nimport { configureEnginePlugin } from '~/src/server/plugins/engine/configureEnginePlugin.js'\nimport pluginErrorPages from '~/src/server/plugins/errorPages.js'\nimport { plugin as pluginViews } from '~/src/server/plugins/nunjucks/index.js'\nimport pluginPulse from '~/src/server/plugins/pulse.js'\nimport pluginSession from '~/src/server/plugins/session.js'\nimport { publicRoutes } from '~/src/server/routes/index.js'\nimport { prepareSecureContext } from '~/src/server/secure-context.js'\nimport { type RouteConfig } from '~/src/server/types.js'\n\nconst proxyAgent = new ProxyAgent()\n\nWreck.agents = {\n https: proxyAgent,\n http: proxyAgent,\n httpsAllowUnauthorized: proxyAgent\n}\n\nconst serverOptions = (): ServerOptions => {\n const serverOptions: ServerOptions = {\n debug: { request: [`${config.get('isDevelopment')}`] },\n port: config.get('port'),\n router: {\n stripTrailingSlash: true\n },\n routes: {\n validate: {\n options: {\n abortEarly: false\n }\n },\n security: {\n hsts: {\n maxAge: 31536000,\n includeSubDomains: true,\n preload: false\n },\n xss: 'enabled',\n noSniff: true,\n xframe: true\n }\n },\n cache: [\n {\n name: 'session',\n engine: config.get('isTest')\n ? new CatboxMemory()\n : new CatboxRedis({\n client: buildRedisClient()\n })\n }\n ]\n }\n\n return serverOptions\n}\n\nexport async function createServer(routeConfig?: RouteConfig) {\n const server = hapi.server(serverOptions())\n\n await server.register(requestLogger)\n\n if (config.get('isProduction')) {\n prepareSecureContext(server)\n }\n\n const pluginCrumb = configureCrumbPlugin(routeConfig)\n const pluginEngine = await configureEnginePlugin(routeConfig)\n\n await server.register(pluginSession)\n await server.register(pluginPulse)\n await server.register(inert)\n await server.register(Scooter)\n await server.register(pluginCrumb)\n\n await server.register(pluginEngine)\n\n server.ext('onPreResponse', (request: Request, h: ResponseToolkit) => {\n const { response } = request\n\n if ('isBoom' in response) {\n return h.continue\n }\n\n // Prevent search engine indexing\n response.header('x-robots-tag', 'noindex, nofollow')\n\n // Disable cache to ensure back/foward navigation updates progress\n if (\n !request.path.startsWith('/javascripts/') &&\n !request.path.startsWith('/stylesheets/') &&\n !request.path.startsWith('/assets/')\n ) {\n response.header('cache-control', 'no-store')\n }\n\n return h.continue\n })\n\n await server.register(pluginViews)\n\n await server.register({\n plugin: {\n name: 'router',\n register: (server) => {\n server.route(publicRoutes)\n }\n }\n })\n\n await server.register(pluginErrorPages)\n await server.register(blipp)\n await server.register(requestTracing)\n\n return server\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -12,7 +12,9 @@ export class AutocompleteField extends SelectField {
|
|
|
12
12
|
if (options.required !== false) {
|
|
13
13
|
const messages = options.customValidationMessages;
|
|
14
14
|
formSchema = formSchema.messages({
|
|
15
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
15
16
|
'any.only': messages?.['any.only'] ?? messageTemplate.required,
|
|
17
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
16
18
|
'any.required': messages?.['any.required'] ?? messageTemplate.required
|
|
17
19
|
});
|
|
18
20
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AutocompleteField.js","names":["SelectField","messageTemplate","AutocompleteField","constructor","def","props","options","formSchema","required","messages","customValidationMessages","getViewModel","payload","errors","viewModel","formGroup","attributes"],"sources":["../../../../../src/server/plugins/engine/components/AutocompleteField.ts"],"sourcesContent":["import { type AutocompleteFieldComponent } from '@defra/forms-model'\n\nimport { SelectField } from '~/src/server/plugins/engine/components/SelectField.js'\nimport { messageTemplate } from '~/src/server/plugins/engine/pageControllers/validationOptions.js'\nimport {\n type FormPayload,\n type FormSubmissionError\n} from '~/src/server/plugins/engine/types.js'\n\nexport class AutocompleteField extends SelectField {\n declare options: AutocompleteFieldComponent['options']\n\n constructor(\n def: AutocompleteFieldComponent,\n props: ConstructorParameters<typeof SelectField>[1]\n ) {\n super(def, props)\n\n const { options } = def\n let { formSchema } = this\n\n if (options.required !== false) {\n const messages = options.customValidationMessages\n\n formSchema = formSchema.messages({\n 'any.only'
|
|
1
|
+
{"version":3,"file":"AutocompleteField.js","names":["SelectField","messageTemplate","AutocompleteField","constructor","def","props","options","formSchema","required","messages","customValidationMessages","getViewModel","payload","errors","viewModel","formGroup","attributes"],"sources":["../../../../../src/server/plugins/engine/components/AutocompleteField.ts"],"sourcesContent":["import { type AutocompleteFieldComponent } from '@defra/forms-model'\n\nimport { SelectField } from '~/src/server/plugins/engine/components/SelectField.js'\nimport { messageTemplate } from '~/src/server/plugins/engine/pageControllers/validationOptions.js'\nimport {\n type FormPayload,\n type FormSubmissionError\n} from '~/src/server/plugins/engine/types.js'\n\nexport class AutocompleteField extends SelectField {\n declare options: AutocompleteFieldComponent['options']\n\n constructor(\n def: AutocompleteFieldComponent,\n props: ConstructorParameters<typeof SelectField>[1]\n ) {\n super(def, props)\n\n const { options } = def\n let { formSchema } = this\n\n if (options.required !== false) {\n const messages = options.customValidationMessages\n\n formSchema = formSchema.messages({\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n 'any.only':\n messages?.['any.only'] ?? (messageTemplate.required as string),\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n 'any.required':\n messages?.['any.required'] ?? (messageTemplate.required as string)\n })\n }\n\n this.options = options\n this.formSchema = formSchema\n }\n\n getViewModel(payload: FormPayload, errors?: FormSubmissionError[]) {\n const viewModel = super.getViewModel(payload, errors)\n let { formGroup } = viewModel\n\n formGroup ??= {}\n formGroup.attributes = {\n 'data-module': 'govuk-accessible-autocomplete'\n }\n\n return {\n ...viewModel,\n formGroup\n }\n }\n}\n"],"mappings":"AAEA,SAASA,WAAW;AACpB,SAASC,eAAe;AAMxB,OAAO,MAAMC,iBAAiB,SAASF,WAAW,CAAC;EAGjDG,WAAWA,CACTC,GAA+B,EAC/BC,KAAmD,EACnD;IACA,KAAK,CAACD,GAAG,EAAEC,KAAK,CAAC;IAEjB,MAAM;MAAEC;IAAQ,CAAC,GAAGF,GAAG;IACvB,IAAI;MAAEG;IAAW,CAAC,GAAG,IAAI;IAEzB,IAAID,OAAO,CAACE,QAAQ,KAAK,KAAK,EAAE;MAC9B,MAAMC,QAAQ,GAAGH,OAAO,CAACI,wBAAwB;MAEjDH,UAAU,GAAGA,UAAU,CAACE,QAAQ,CAAC;QAC/B;QACA,UAAU,EACRA,QAAQ,GAAG,UAAU,CAAC,IAAKR,eAAe,CAACO,QAAmB;QAChE;QACA,cAAc,EACZC,QAAQ,GAAG,cAAc,CAAC,IAAKR,eAAe,CAACO;MACnD,CAAC,CAAC;IACJ;IAEA,IAAI,CAACF,OAAO,GAAGA,OAAO;IACtB,IAAI,CAACC,UAAU,GAAGA,UAAU;EAC9B;EAEAI,YAAYA,CAACC,OAAoB,EAAEC,MAA8B,EAAE;IACjE,MAAMC,SAAS,GAAG,KAAK,CAACH,YAAY,CAACC,OAAO,EAAEC,MAAM,CAAC;IACrD,IAAI;MAAEE;IAAU,CAAC,GAAGD,SAAS;IAE7BC,SAAS,KAAK,CAAC,CAAC;IAChBA,SAAS,CAACC,UAAU,GAAG;MACrB,aAAa,EAAE;IACjB,CAAC;IAED,OAAO;MACL,GAAGF,SAAS;MACZC;IACF,CAAC;EACH;AACF","ignoreList":[]}
|
|
@@ -8,12 +8,11 @@ export class CheckboxesField extends SelectionControlField {
|
|
|
8
8
|
listType: type
|
|
9
9
|
} = this;
|
|
10
10
|
const {
|
|
11
|
-
options
|
|
12
|
-
title
|
|
11
|
+
options
|
|
13
12
|
} = def;
|
|
14
13
|
let formSchema = type === 'string' ? joi.array() : joi.array();
|
|
15
|
-
const itemsSchema = joi[type]().valid(...this.values).label(
|
|
16
|
-
formSchema = formSchema.items(itemsSchema).single().label(
|
|
14
|
+
const itemsSchema = joi[type]().valid(...this.values).label(this.label);
|
|
15
|
+
formSchema = formSchema.items(itemsSchema).single().label(this.label).required();
|
|
17
16
|
if (options.required === false) {
|
|
18
17
|
formSchema = formSchema.optional();
|
|
19
18
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CheckboxesField.js","names":["joi","isFormValue","SelectionControlField","CheckboxesField","constructor","def","props","listType","type","options","
|
|
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","getDisplayStringFromState","text","join","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 getDisplayStringFromState(state: FormSubmissionState) {\n const { items } = this\n\n // Selected checkbox values\n const selected = this.getFormValueFromState(state) ?? []\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 getContextValueFromState(state: FormSubmissionState) {\n const values = this.getFormValueFromState(state)\n\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 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,yBAAyBA,CAACZ,KAA0B,EAAE;IACpD,MAAM;MAAER;IAAM,CAAC,GAAG,IAAI;;IAEtB;IACA,MAAMW,QAAQ,GAAG,IAAI,CAACJ,qBAAqB,CAACC,KAAK,CAAC,IAAI,EAAE;;IAExD;IACA,OAAOR,KAAK,CACTY,MAAM,CAAEC,IAAI,IAAKF,QAAQ,CAACG,QAAQ,CAACD,IAAI,CAACE,KAAK,CAAC,CAAC,CAC/CC,GAAG,CAAEH,IAAI,IAAKA,IAAI,CAACQ,IAAI,CAAC,CACxBC,IAAI,CAAC,IAAI,CAAC;EACf;EAEAC,wBAAwBA,CAACf,KAA0B,EAAE;IACnD,MAAMV,MAAM,GAAG,IAAI,CAACS,qBAAqB,CAACC,KAAK,CAAC;;IAEhD;AACJ;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;IACI,OAAOV,MAAM,IAAI,EAAE;EACrB;EAEAqB,OAAOA,CAACJ,KAAkC,EAA4B;IACpE,IAAI,CAACS,KAAK,CAACC,OAAO,CAACV,KAAK,CAAC,EAAE;MACzB,OAAO,KAAK;IACd;;IAEA;IACA,IAAI,CAACA,KAAK,CAACE,MAAM,EAAE;MACjB,OAAO,IAAI;IACb;IAEA,OAAOF,KAAK,CAACW,KAAK,CAACzC,WAAW,CAAC;EACjC;AACF","ignoreList":[]}
|
|
@@ -68,7 +68,7 @@ export class ComponentCollection {
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
// Update error with parent title
|
|
71
|
-
error.local.title ??= parent?.
|
|
71
|
+
error.local.title ??= parent?.label;
|
|
72
72
|
return error;
|
|
73
73
|
});
|
|
74
74
|
});
|
|
@@ -139,23 +139,21 @@ export class ComponentCollection {
|
|
|
139
139
|
}
|
|
140
140
|
return context;
|
|
141
141
|
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Get all errors for all fields in this collection
|
|
145
|
+
*/
|
|
142
146
|
getErrors(errors) {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
} = this;
|
|
146
|
-
const list = [];
|
|
147
|
+
return this.getFieldErrors(field => field.getErrors(errors), errors);
|
|
148
|
+
}
|
|
147
149
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
if (!list.length) {
|
|
156
|
-
return;
|
|
157
|
-
}
|
|
158
|
-
return list;
|
|
150
|
+
/**
|
|
151
|
+
* Get view errors for all fields in this collection.
|
|
152
|
+
* For most fields this means filtering to the first error in the list.
|
|
153
|
+
* Composite fields like UKAddress can choose to return more than one error.
|
|
154
|
+
*/
|
|
155
|
+
getViewErrors(errors) {
|
|
156
|
+
return this.getFieldErrors(field => field.getViewErrors(errors), errors);
|
|
159
157
|
}
|
|
160
158
|
getViewModel(payload, errors, query = {}) {
|
|
161
159
|
const {
|
|
@@ -187,6 +185,29 @@ export class ComponentCollection {
|
|
|
187
185
|
errors: this.page?.getErrors(details) ?? getErrors(details)
|
|
188
186
|
};
|
|
189
187
|
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Helper to get errors from all fields
|
|
191
|
+
*/
|
|
192
|
+
getFieldErrors(callback, errors) {
|
|
193
|
+
const {
|
|
194
|
+
fields
|
|
195
|
+
} = this;
|
|
196
|
+
if (!errors?.length) {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
const list = [];
|
|
200
|
+
for (const field of fields) {
|
|
201
|
+
const fieldErrors = callback(field);
|
|
202
|
+
if (fieldErrors?.length) {
|
|
203
|
+
list.push(...fieldErrors);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
if (!list.length) {
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
return list;
|
|
210
|
+
}
|
|
190
211
|
}
|
|
191
212
|
|
|
192
213
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ComponentCollection.js","names":["joi","FormComponent","isFormState","isFormValue","createComponent","getErrors","validationOptions","opts","ComponentCollection","page","parent","components","fields","guidance","formSchema","stateSchema","constructor","defs","props","schema","map","def","filter","component","isFormComponent","object","required","field","collection","name","concat","keys","error","errors","flatMap","isErrorContext","local","title","missing","key","path","find","item","split","shift","child","label","length","peers","and","isPresent","custom","getFormDataFromState","state","payload","forEach","Object","assign","getFormValueFromState","value","entries","pop","getStateFromValidForm","getContextValueFromState","context","list","getError","push","getViewModel","query","result","type","model","validate","details"],"sources":["../../../../../src/server/plugins/engine/components/ComponentCollection.ts"],"sourcesContent":["import { type ComponentDef } from '@defra/forms-model'\nimport joi, {\n type CustomValidator,\n type ErrorReportCollection,\n type ObjectSchema\n} from 'joi'\n\nimport {\n FormComponent,\n isFormState,\n isFormValue\n} from '~/src/server/plugins/engine/components/FormComponent.js'\nimport {\n createComponent,\n type Component,\n type Field,\n type Guidance\n} from '~/src/server/plugins/engine/components/helpers.js'\nimport { type ComponentViewModel } from '~/src/server/plugins/engine/components/types.js'\nimport { getErrors } from '~/src/server/plugins/engine/helpers.js'\nimport { type FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport { type PageControllerClass } from '~/src/server/plugins/engine/pageControllers/helpers.js'\nimport { validationOptions as opts } from '~/src/server/plugins/engine/pageControllers/validationOptions.js'\nimport {\n type FormPayload,\n type FormState,\n type FormSubmissionError,\n type FormSubmissionState,\n type FormValidationResult\n} from '~/src/server/plugins/engine/types.js'\nimport { type FormQuery } from '~/src/server/routes/types.js'\n\nexport class ComponentCollection {\n page?: PageControllerClass\n parent?: Component\n\n components: Component[]\n fields: Field[]\n guidance: Guidance[]\n\n formSchema: ObjectSchema<FormPayload>\n stateSchema: ObjectSchema<FormSubmissionState>\n\n constructor(\n defs: ComponentDef[],\n props: {\n page?: PageControllerClass\n parent?: Component\n model: FormModel\n },\n schema?: {\n /**\n * Defines an all-or-nothing relationship between keys where if one\n * of the peers is present, all of them are required as well\n */\n peers?: string[]\n\n /**\n * Defines a custom validation rule for the object schema\n */\n custom?: CustomValidator\n }\n ) {\n const components = defs.map((def) => createComponent(def, props))\n\n const fields = components.filter(\n (component): component is Field => component.isFormComponent\n )\n\n const guidance = components.filter(\n (component): component is Guidance => !component.isFormComponent\n )\n\n let formSchema = joi.object<FormPayload>().required()\n let stateSchema = joi.object<FormSubmissionState>().required()\n\n // Add each field or concat collection\n for (const field of fields) {\n const { collection, name } = field\n\n formSchema = collection\n ? formSchema.concat(collection.formSchema)\n : formSchema.keys({ [name]: field.formSchema })\n\n stateSchema = collection\n ? stateSchema.concat(collection.stateSchema)\n : stateSchema.keys({ [name]: field.stateSchema })\n }\n\n // Add parent field title to collection field errors\n formSchema = formSchema.error((errors) => {\n return errors.flatMap((error) => {\n if (!isErrorContext(error.local) || error.local.title) {\n return error\n }\n\n // Use field key or first missing child field\n let { missing, key = missing?.[0] } = error.local\n\n // But avoid numeric key used by array payloads\n if (typeof key === 'number') {\n key = error.path[0]\n }\n\n // Find the parent field\n const parent = fields.find(\n (item) => item.name === key?.split('__').shift()\n )\n\n // Find the child field\n const child = (parent?.collection?.fields ?? fields).find(\n (item) => item.name === key\n )\n\n // Update error with child label\n if (child && (!error.local.label || error.local.label === 'value')) {\n error.local.label = child.title\n }\n\n // Fix error summary links for missing fields\n if (missing?.length) {\n error.path = missing\n error.local.key = missing[0]\n }\n\n // Update error with parent title\n error.local.title ??= parent?.title\n\n return error\n })\n })\n\n if (schema?.peers) {\n formSchema = formSchema.and(...schema.peers, {\n isPresent: isFormValue\n })\n }\n\n if (schema?.custom) {\n formSchema = formSchema.custom(schema.custom)\n }\n\n this.page = props.page\n this.parent = props.parent\n\n this.components = components\n this.fields = fields\n this.guidance = guidance\n\n this.formSchema = formSchema\n this.stateSchema = stateSchema\n }\n\n get keys() {\n return this.fields.flatMap((field) => {\n const { name, collection } = field\n\n if (collection) {\n const { fields } = collection\n return [name, ...fields.map(({ name }) => name)]\n }\n\n return [name]\n })\n }\n\n getFormDataFromState(state: FormSubmissionState) {\n const payload: FormPayload = {}\n\n this.fields.forEach((component) => {\n Object.assign(payload, component.getFormDataFromState(state))\n })\n\n return payload\n }\n\n getFormValueFromState(state: FormSubmissionState) {\n const payload: FormPayload = {}\n\n // Remove name prefix for formatted value\n for (const [name, value] of Object.entries(\n this.getFormDataFromState(state)\n )) {\n const key = name.split('__').pop()\n if (!key) {\n continue\n }\n\n payload[key] = value\n }\n\n return payload\n }\n\n getStateFromValidForm(payload: FormPayload) {\n const state: FormState = {}\n\n this.fields.forEach((component) => {\n Object.assign(state, component.getStateFromValidForm(payload))\n })\n\n return state\n }\n\n getContextValueFromState(state: FormSubmissionState) {\n const context: FormState = {}\n\n for (const component of this.fields) {\n context[component.name] = component.getContextValueFromState(state)\n }\n\n return context\n }\n\n getErrors(errors?: FormSubmissionError[]): FormSubmissionError[] | undefined {\n const { fields } = this\n\n const list: FormSubmissionError[] = []\n\n // Add only one error per field\n for (const field of fields) {\n const error = field.getError(errors)\n\n if (error) {\n list.push(error)\n }\n }\n\n if (!list.length) {\n return\n }\n\n return list\n }\n\n getViewModel(\n payload: FormPayload,\n errors?: FormSubmissionError[],\n query: FormQuery = {}\n ) {\n const { components } = this\n\n const result: ComponentViewModel[] = components.map((component) => {\n const { isFormComponent, type } = component\n\n const model =\n component instanceof FormComponent\n ? component.getViewModel(payload, errors, query)\n : component.getViewModel()\n\n return { type, isFormComponent, model }\n })\n\n return result\n }\n\n /**\n * Validate form payload\n */\n validate(value: FormPayload = {}): FormValidationResult<FormPayload> {\n const result = this.formSchema.validate(value, opts)\n const details = result.error?.details\n\n return {\n value: (result.value ?? {}) as typeof value,\n errors: this.page?.getErrors(details) ?? getErrors(details)\n }\n }\n}\n\n/**\n * Check for field local state\n */\nexport function isErrorContext(\n value?: unknown\n): value is ErrorReportCollection['local'] {\n return isFormState(value) && typeof value.label === 'string'\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"ComponentCollection.js","names":["joi","FormComponent","isFormState","isFormValue","createComponent","getErrors","validationOptions","opts","ComponentCollection","page","parent","components","fields","guidance","formSchema","stateSchema","constructor","defs","props","schema","map","def","filter","component","isFormComponent","object","required","field","collection","name","concat","keys","error","errors","flatMap","isErrorContext","local","title","missing","key","path","find","item","split","shift","child","label","length","peers","and","isPresent","custom","getFormDataFromState","state","payload","forEach","Object","assign","getFormValueFromState","value","entries","pop","getStateFromValidForm","getContextValueFromState","context","getFieldErrors","getViewErrors","getViewModel","query","result","type","model","validate","details","callback","list","fieldErrors","push"],"sources":["../../../../../src/server/plugins/engine/components/ComponentCollection.ts"],"sourcesContent":["import { type ComponentDef } from '@defra/forms-model'\nimport joi, {\n type CustomValidator,\n type ErrorReportCollection,\n type ObjectSchema\n} from 'joi'\n\nimport {\n FormComponent,\n isFormState,\n isFormValue\n} from '~/src/server/plugins/engine/components/FormComponent.js'\nimport {\n createComponent,\n type Component,\n type Field,\n type Guidance\n} from '~/src/server/plugins/engine/components/helpers.js'\nimport { type ComponentViewModel } from '~/src/server/plugins/engine/components/types.js'\nimport { getErrors } from '~/src/server/plugins/engine/helpers.js'\nimport { type FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport { type PageControllerClass } from '~/src/server/plugins/engine/pageControllers/helpers.js'\nimport { validationOptions as opts } from '~/src/server/plugins/engine/pageControllers/validationOptions.js'\nimport {\n type FormPayload,\n type FormState,\n type FormSubmissionError,\n type FormSubmissionState,\n type FormValidationResult\n} from '~/src/server/plugins/engine/types.js'\nimport { type FormQuery } from '~/src/server/routes/types.js'\n\nexport class ComponentCollection {\n page?: PageControllerClass\n parent?: Component\n\n components: Component[]\n fields: Field[]\n guidance: Guidance[]\n\n formSchema: ObjectSchema<FormPayload>\n stateSchema: ObjectSchema<FormSubmissionState>\n\n constructor(\n defs: ComponentDef[],\n props: {\n page?: PageControllerClass\n parent?: Component\n model: FormModel\n },\n schema?: {\n /**\n * Defines an all-or-nothing relationship between keys where if one\n * of the peers is present, all of them are required as well\n */\n peers?: string[]\n\n /**\n * Defines a custom validation rule for the object schema\n */\n custom?: CustomValidator\n }\n ) {\n const components = defs.map((def) => createComponent(def, props))\n\n const fields = components.filter(\n (component): component is Field => component.isFormComponent\n )\n\n const guidance = components.filter(\n (component): component is Guidance => !component.isFormComponent\n )\n\n let formSchema = joi.object<FormPayload>().required()\n let stateSchema = joi.object<FormSubmissionState>().required()\n\n // Add each field or concat collection\n for (const field of fields) {\n const { collection, name } = field\n\n formSchema = collection\n ? formSchema.concat(collection.formSchema)\n : formSchema.keys({ [name]: field.formSchema })\n\n stateSchema = collection\n ? stateSchema.concat(collection.stateSchema)\n : stateSchema.keys({ [name]: field.stateSchema })\n }\n\n // Add parent field title to collection field errors\n formSchema = formSchema.error((errors) => {\n return errors.flatMap((error) => {\n if (!isErrorContext(error.local) || error.local.title) {\n return error\n }\n\n // Use field key or first missing child field\n let { missing, key = missing?.[0] } = error.local\n\n // But avoid numeric key used by array payloads\n if (typeof key === 'number') {\n key = error.path[0]\n }\n\n // Find the parent field\n const parent = fields.find(\n (item) => item.name === key?.split('__').shift()\n )\n\n // Find the child field\n const child = (parent?.collection?.fields ?? fields).find(\n (item) => item.name === key\n )\n\n // Update error with child label\n if (child && (!error.local.label || error.local.label === 'value')) {\n error.local.label = child.title\n }\n\n // Fix error summary links for missing fields\n if (missing?.length) {\n error.path = missing\n error.local.key = missing[0]\n }\n\n // Update error with parent title\n error.local.title ??= parent?.label\n\n return error\n })\n })\n\n if (schema?.peers) {\n formSchema = formSchema.and(...schema.peers, {\n isPresent: isFormValue\n })\n }\n\n if (schema?.custom) {\n formSchema = formSchema.custom(schema.custom)\n }\n\n this.page = props.page\n this.parent = props.parent\n\n this.components = components\n this.fields = fields\n this.guidance = guidance\n\n this.formSchema = formSchema\n this.stateSchema = stateSchema\n }\n\n get keys() {\n return this.fields.flatMap((field) => {\n const { name, collection } = field\n\n if (collection) {\n const { fields } = collection\n return [name, ...fields.map(({ name }) => name)]\n }\n\n return [name]\n })\n }\n\n getFormDataFromState(state: FormSubmissionState) {\n const payload: FormPayload = {}\n\n this.fields.forEach((component) => {\n Object.assign(payload, component.getFormDataFromState(state))\n })\n\n return payload\n }\n\n getFormValueFromState(state: FormSubmissionState) {\n const payload: FormPayload = {}\n\n // Remove name prefix for formatted value\n for (const [name, value] of Object.entries(\n this.getFormDataFromState(state)\n )) {\n const key = name.split('__').pop()\n if (!key) {\n continue\n }\n\n payload[key] = value\n }\n\n return payload\n }\n\n getStateFromValidForm(payload: FormPayload) {\n const state: FormState = {}\n\n this.fields.forEach((component) => {\n Object.assign(state, component.getStateFromValidForm(payload))\n })\n\n return state\n }\n\n getContextValueFromState(state: FormSubmissionState) {\n const context: FormState = {}\n\n for (const component of this.fields) {\n context[component.name] = component.getContextValueFromState(state)\n }\n\n return context\n }\n\n /**\n * Get all errors for all fields in this collection\n */\n getErrors(errors?: FormSubmissionError[]): FormSubmissionError[] | undefined {\n return this.getFieldErrors((field) => field.getErrors(errors), errors)\n }\n\n /**\n * Get view errors for all fields in this collection.\n * For most fields this means filtering to the first error in the list.\n * Composite fields like UKAddress can choose to return more than one error.\n */\n getViewErrors(\n errors?: FormSubmissionError[]\n ): FormSubmissionError[] | undefined {\n return this.getFieldErrors((field) => field.getViewErrors(errors), errors)\n }\n\n getViewModel(\n payload: FormPayload,\n errors?: FormSubmissionError[],\n query: FormQuery = {}\n ) {\n const { components } = this\n\n const result: ComponentViewModel[] = components.map((component) => {\n const { isFormComponent, type } = component\n\n const model =\n component instanceof FormComponent\n ? component.getViewModel(payload, errors, query)\n : component.getViewModel()\n\n return { type, isFormComponent, model }\n })\n\n return result\n }\n\n /**\n * Validate form payload\n */\n validate(value: FormPayload = {}): FormValidationResult<FormPayload> {\n const result = this.formSchema.validate(value, opts)\n const details = result.error?.details\n\n return {\n value: (result.value ?? {}) as typeof value,\n errors: this.page?.getErrors(details) ?? getErrors(details)\n }\n }\n\n /**\n * Helper to get errors from all fields\n */\n private getFieldErrors(\n callback: (field: Field) => FormSubmissionError[] | undefined,\n errors?: FormSubmissionError[]\n ): FormSubmissionError[] | undefined {\n const { fields } = this\n\n if (!errors?.length) {\n return\n }\n\n const list: FormSubmissionError[] = []\n\n for (const field of fields) {\n const fieldErrors = callback(field)\n\n if (fieldErrors?.length) {\n list.push(...fieldErrors)\n }\n }\n\n if (!list.length) {\n return\n }\n\n return list\n }\n}\n\n/**\n * Check for field local state\n */\nexport function isErrorContext(\n value?: unknown\n): value is ErrorReportCollection['local'] {\n return isFormState(value) && typeof value.label === 'string'\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -4,6 +4,7 @@ import { ComponentCollection } from "./ComponentCollection.js";
|
|
|
4
4
|
import { FormComponent, isFormState, isFormValue } from "./FormComponent.js";
|
|
5
5
|
import { NumberField } from "./NumberField.js";
|
|
6
6
|
import { messageTemplate } from "../pageControllers/validationOptions.js";
|
|
7
|
+
import { convertToLanguageMessages } from "../../../utils/type-utils.js";
|
|
7
8
|
export class DatePartsField extends FormComponent {
|
|
8
9
|
constructor(def, props) {
|
|
9
10
|
super(def, props);
|
|
@@ -12,15 +13,17 @@ export class DatePartsField extends FormComponent {
|
|
|
12
13
|
options
|
|
13
14
|
} = def;
|
|
14
15
|
const isRequired = options.required !== false;
|
|
15
|
-
const customValidationMessages = {
|
|
16
|
+
const customValidationMessages = convertToLanguageMessages({
|
|
17
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
16
18
|
'any.required': messageTemplate.objectMissing,
|
|
19
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
17
20
|
'number.base': messageTemplate.objectMissing,
|
|
18
21
|
'number.precision': messageTemplate.dateFormat,
|
|
19
22
|
'number.integer': messageTemplate.dateFormat,
|
|
20
23
|
'number.unsafe': messageTemplate.dateFormat,
|
|
21
24
|
'number.min': messageTemplate.dateFormat,
|
|
22
25
|
'number.max': messageTemplate.dateFormat
|
|
23
|
-
};
|
|
26
|
+
});
|
|
24
27
|
this.collection = new ComponentCollection([{
|
|
25
28
|
type: ComponentType.NumberField,
|
|
26
29
|
name: `${name}__day`,
|
|
@@ -156,6 +159,37 @@ export class DatePartsField extends FormComponent {
|
|
|
156
159
|
isState(value) {
|
|
157
160
|
return DatePartsField.isDateParts(value);
|
|
158
161
|
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* For error preview page that shows all possible errors on a component
|
|
165
|
+
*/
|
|
166
|
+
getAllPossibleErrors() {
|
|
167
|
+
return {
|
|
168
|
+
baseErrors: [{
|
|
169
|
+
type: 'required',
|
|
170
|
+
template: messageTemplate.required
|
|
171
|
+
}, {
|
|
172
|
+
type: 'dateFormat',
|
|
173
|
+
template: messageTemplate.dateFormat
|
|
174
|
+
}, {
|
|
175
|
+
type: 'dateFormatDay',
|
|
176
|
+
template: '{{#label}} must include a day'
|
|
177
|
+
}, {
|
|
178
|
+
type: 'dateFormatMonth',
|
|
179
|
+
template: '{{#label}} must include a month'
|
|
180
|
+
}, {
|
|
181
|
+
type: 'dateFormatYear',
|
|
182
|
+
template: '{{#label}} must include a year'
|
|
183
|
+
}],
|
|
184
|
+
advancedSettingsErrors: [{
|
|
185
|
+
type: 'dateMin',
|
|
186
|
+
template: messageTemplate.dateMin
|
|
187
|
+
}, {
|
|
188
|
+
type: 'dateMax',
|
|
189
|
+
template: messageTemplate.dateMax
|
|
190
|
+
}]
|
|
191
|
+
};
|
|
192
|
+
}
|
|
159
193
|
static isDateParts(value) {
|
|
160
194
|
return isFormState(value) && NumberField.isNumber(value.day) && NumberField.isNumber(value.month) && NumberField.isNumber(value.year);
|
|
161
195
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DatePartsField.js","names":["ComponentType","add","format","isValid","parse","startOfToday","sub","ComponentCollection","FormComponent","isFormState","isFormValue","NumberField","messageTemplate","DatePartsField","constructor","def","props","name","options","isRequired","required","customValidationMessages","objectMissing","dateFormat","collection","type","title","schema","min","max","precision","optionalText","classes","parent","custom","getValidatorDate","peers","formSchema","stateSchema","getFormValueFromState","state","value","isState","undefined","getDisplayStringFromState","year","month","day","getContextValueFromState","Date","getViewModel","payload","errors","viewModel","fieldset","label","hasError","some","error","items","map","model","errorMessage","toString","text","trim","id","legend","isDateParts","isNumber","component","validator","helpers","values","getStateFromValidForm","context","missing","keys","key","date","dateMin","maxDaysInPast","days","dateMax","maxDaysInFuture","limit"],"sources":["../../../../../src/server/plugins/engine/components/DatePartsField.ts"],"sourcesContent":["import { ComponentType, type DatePartsFieldComponent } from '@defra/forms-model'\nimport { add, format, isValid, parse, startOfToday, sub } from 'date-fns'\nimport {\n type Context,\n type CustomValidator,\n type LanguageMessages,\n type ObjectSchema\n} from 'joi'\n\nimport { ComponentCollection } from '~/src/server/plugins/engine/components/ComponentCollection.js'\nimport {\n FormComponent,\n isFormState,\n isFormValue\n} from '~/src/server/plugins/engine/components/FormComponent.js'\nimport { NumberField } from '~/src/server/plugins/engine/components/NumberField.js'\nimport { type DateInputItem } from '~/src/server/plugins/engine/components/types.js'\nimport { messageTemplate } from '~/src/server/plugins/engine/pageControllers/validationOptions.js'\nimport {\n type FormPayload,\n type FormState,\n type FormStateValue,\n type FormSubmissionError,\n type FormSubmissionState\n} from '~/src/server/plugins/engine/types.js'\n\nexport class DatePartsField extends FormComponent {\n declare options: DatePartsFieldComponent['options']\n declare formSchema: ObjectSchema<FormPayload>\n declare stateSchema: ObjectSchema<FormState>\n declare collection: ComponentCollection\n\n constructor(\n def: DatePartsFieldComponent,\n props: ConstructorParameters<typeof FormComponent>[1]\n ) {\n super(def, props)\n\n const { name, options } = def\n\n const isRequired = options.required !== false\n\n const customValidationMessages: LanguageMessages = {\n 'any.required': messageTemplate.objectMissing,\n 'number.base': messageTemplate.objectMissing,\n 'number.precision': messageTemplate.dateFormat,\n 'number.integer': messageTemplate.dateFormat,\n 'number.unsafe': messageTemplate.dateFormat,\n 'number.min': messageTemplate.dateFormat,\n 'number.max': messageTemplate.dateFormat\n }\n\n this.collection = new ComponentCollection(\n [\n {\n type: ComponentType.NumberField,\n name: `${name}__day`,\n title: 'Day',\n schema: { min: 1, max: 31, precision: 0 },\n options: {\n required: isRequired,\n optionalText: true,\n classes: 'govuk-input--width-2',\n customValidationMessages\n }\n },\n {\n type: ComponentType.NumberField,\n name: `${name}__month`,\n title: 'Month',\n schema: { min: 1, max: 12, precision: 0 },\n options: {\n required: isRequired,\n optionalText: true,\n classes: 'govuk-input--width-2',\n customValidationMessages\n }\n },\n {\n type: ComponentType.NumberField,\n name: `${name}__year`,\n title: 'Year',\n schema: { min: 1000, max: 3000, precision: 0 },\n options: {\n required: isRequired,\n optionalText: true,\n classes: 'govuk-input--width-4',\n customValidationMessages\n }\n }\n ],\n { ...props, parent: this },\n {\n custom: getValidatorDate(this),\n peers: [`${name}__day`, `${name}__month`, `${name}__year`]\n }\n )\n\n this.options = options\n this.formSchema = this.collection.formSchema\n this.stateSchema = this.collection.stateSchema\n }\n\n getFormValueFromState(state: FormSubmissionState) {\n const value = super.getFormValueFromState(state)\n return this.isState(value) ? value : undefined\n }\n\n getDisplayStringFromState(state: FormSubmissionState) {\n const value = this.getFormValueFromState(state)\n\n if (!value) {\n return ''\n }\n\n return format(`${value.year}-${value.month}-${value.day}`, 'd MMMM yyyy')\n }\n\n getContextValueFromState(state: FormSubmissionState) {\n const value = this.getFormValueFromState(state)\n\n if (\n !value ||\n !isValid(\n parse(\n `${value.year}-${value.month}-${value.day}`,\n 'yyyy-MM-dd',\n new Date()\n )\n )\n ) {\n return null\n }\n\n return format(`${value.year}-${value.month}-${value.day}`, 'yyyy-MM-dd')\n }\n\n getViewModel(payload: FormPayload, errors?: FormSubmissionError[]) {\n const { collection, name } = this\n\n const viewModel = super.getViewModel(payload, errors)\n let { fieldset, label } = viewModel\n\n // Check for component errors only\n const hasError = errors?.some((error) => error.name === name)\n\n // Use the component collection to generate the subitems\n const items: DateInputItem[] = collection\n .getViewModel(payload, errors)\n .map(({ model }) => {\n let { label, type, value, classes, errorMessage } = model\n\n if (label) {\n label.toString = () => label.text // Date component uses string labels\n }\n\n if (hasError || errorMessage) {\n classes = `${classes} govuk-input--error`.trim()\n }\n\n // Allow any `toString()`-able value so non-numeric\n // values are shown alongside their error messages\n if (!isFormValue(value)) {\n value = undefined\n }\n\n return {\n label,\n id: model.id,\n name: model.name,\n type,\n value,\n classes\n }\n })\n\n fieldset ??= {\n legend: {\n text: label.text,\n classes: 'govuk-fieldset__legend--m'\n }\n }\n\n return {\n ...viewModel,\n fieldset,\n items\n }\n }\n\n isState(value?: FormStateValue | FormState) {\n return DatePartsField.isDateParts(value)\n }\n\n static isDateParts(\n value?: FormStateValue | FormState\n ): value is DatePartsState {\n return (\n isFormState(value) &&\n NumberField.isNumber(value.day) &&\n NumberField.isNumber(value.month) &&\n NumberField.isNumber(value.year)\n )\n }\n}\n\nexport interface DatePartsState extends Record<string, number> {\n day: number\n month: number\n year: number\n}\n\nexport function getValidatorDate(component: DatePartsField) {\n const validator: CustomValidator = (payload: FormPayload, helpers) => {\n const { collection, name, options } = component\n\n const values = component.getFormValueFromState(\n component.getStateFromValidForm(payload)\n )\n\n const context: Context = {\n missing: collection.keys,\n key: name\n }\n\n if (!component.isState(values)) {\n return options.required !== false\n ? helpers.error('object.required', context)\n : payload\n }\n\n const date = parse(\n `${values.year}-${values.month}-${values.day}`,\n 'yyyy-MM-dd',\n new Date()\n )\n\n if (!isValid(date)) {\n return helpers.error('date.format', context)\n }\n\n // Minimum date from today\n const dateMin = options.maxDaysInPast\n ? sub(startOfToday(), { days: options.maxDaysInPast })\n : undefined\n\n // Maximum date from today\n const dateMax = options.maxDaysInFuture\n ? add(startOfToday(), { days: options.maxDaysInFuture })\n : undefined\n\n if (dateMin && date < dateMin) {\n return helpers.error('date.min', { ...context, limit: dateMin })\n }\n\n if (dateMax && date > dateMax) {\n return helpers.error('date.max', { ...context, limit: dateMax })\n }\n\n return payload\n }\n\n return validator\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"DatePartsField.js","names":["ComponentType","add","format","isValid","parse","startOfToday","sub","ComponentCollection","FormComponent","isFormState","isFormValue","NumberField","messageTemplate","convertToLanguageMessages","DatePartsField","constructor","def","props","name","options","isRequired","required","customValidationMessages","objectMissing","dateFormat","collection","type","title","schema","min","max","precision","optionalText","classes","parent","custom","getValidatorDate","peers","formSchema","stateSchema","getFormValueFromState","state","value","isState","undefined","getDisplayStringFromState","year","month","day","getContextValueFromState","Date","getViewModel","payload","errors","viewModel","fieldset","label","hasError","some","error","items","map","model","errorMessage","toString","text","trim","id","legend","isDateParts","getAllPossibleErrors","baseErrors","template","advancedSettingsErrors","dateMin","dateMax","isNumber","component","validator","helpers","values","getStateFromValidForm","context","missing","keys","key","date","maxDaysInPast","days","maxDaysInFuture","limit"],"sources":["../../../../../src/server/plugins/engine/components/DatePartsField.ts"],"sourcesContent":["import { ComponentType, type DatePartsFieldComponent } from '@defra/forms-model'\nimport { add, format, isValid, parse, startOfToday, sub } from 'date-fns'\nimport { type Context, type CustomValidator, type ObjectSchema } from 'joi'\n\nimport { ComponentCollection } from '~/src/server/plugins/engine/components/ComponentCollection.js'\nimport {\n FormComponent,\n isFormState,\n isFormValue\n} from '~/src/server/plugins/engine/components/FormComponent.js'\nimport { NumberField } from '~/src/server/plugins/engine/components/NumberField.js'\nimport { type DateInputItem } from '~/src/server/plugins/engine/components/types.js'\nimport { messageTemplate } from '~/src/server/plugins/engine/pageControllers/validationOptions.js'\nimport {\n type ErrorMessageTemplateList,\n type FormPayload,\n type FormState,\n type FormStateValue,\n type FormSubmissionError,\n type FormSubmissionState\n} from '~/src/server/plugins/engine/types.js'\nimport { convertToLanguageMessages } from '~/src/server/utils/type-utils.js'\n\nexport class DatePartsField extends FormComponent {\n declare options: DatePartsFieldComponent['options']\n declare formSchema: ObjectSchema<FormPayload>\n declare stateSchema: ObjectSchema<FormState>\n declare collection: ComponentCollection\n\n constructor(\n def: DatePartsFieldComponent,\n props: ConstructorParameters<typeof FormComponent>[1]\n ) {\n super(def, props)\n\n const { name, options } = def\n\n const isRequired = options.required !== false\n\n const customValidationMessages = convertToLanguageMessages({\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n 'any.required': messageTemplate.objectMissing,\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n 'number.base': messageTemplate.objectMissing,\n 'number.precision': messageTemplate.dateFormat,\n 'number.integer': messageTemplate.dateFormat,\n 'number.unsafe': messageTemplate.dateFormat,\n 'number.min': messageTemplate.dateFormat,\n 'number.max': messageTemplate.dateFormat\n })\n\n this.collection = new ComponentCollection(\n [\n {\n type: ComponentType.NumberField,\n name: `${name}__day`,\n title: 'Day',\n schema: { min: 1, max: 31, precision: 0 },\n options: {\n required: isRequired,\n optionalText: true,\n classes: 'govuk-input--width-2',\n customValidationMessages\n }\n },\n {\n type: ComponentType.NumberField,\n name: `${name}__month`,\n title: 'Month',\n schema: { min: 1, max: 12, precision: 0 },\n options: {\n required: isRequired,\n optionalText: true,\n classes: 'govuk-input--width-2',\n customValidationMessages\n }\n },\n {\n type: ComponentType.NumberField,\n name: `${name}__year`,\n title: 'Year',\n schema: { min: 1000, max: 3000, precision: 0 },\n options: {\n required: isRequired,\n optionalText: true,\n classes: 'govuk-input--width-4',\n customValidationMessages\n }\n }\n ],\n { ...props, parent: this },\n {\n custom: getValidatorDate(this),\n peers: [`${name}__day`, `${name}__month`, `${name}__year`]\n }\n )\n\n this.options = options\n this.formSchema = this.collection.formSchema\n this.stateSchema = this.collection.stateSchema\n }\n\n getFormValueFromState(state: FormSubmissionState) {\n const value = super.getFormValueFromState(state)\n return this.isState(value) ? value : undefined\n }\n\n getDisplayStringFromState(state: FormSubmissionState) {\n const value = this.getFormValueFromState(state)\n\n if (!value) {\n return ''\n }\n\n return format(`${value.year}-${value.month}-${value.day}`, 'd MMMM yyyy')\n }\n\n getContextValueFromState(state: FormSubmissionState) {\n const value = this.getFormValueFromState(state)\n\n if (\n !value ||\n !isValid(\n parse(\n `${value.year}-${value.month}-${value.day}`,\n 'yyyy-MM-dd',\n new Date()\n )\n )\n ) {\n return null\n }\n\n return format(`${value.year}-${value.month}-${value.day}`, 'yyyy-MM-dd')\n }\n\n getViewModel(payload: FormPayload, errors?: FormSubmissionError[]) {\n const { collection, name } = this\n\n const viewModel = super.getViewModel(payload, errors)\n let { fieldset, label } = viewModel\n\n // Check for component errors only\n const hasError = errors?.some((error) => error.name === name)\n\n // Use the component collection to generate the subitems\n const items: DateInputItem[] = collection\n .getViewModel(payload, errors)\n .map(({ model }) => {\n let { label, type, value, classes, errorMessage } = model\n\n if (label) {\n label.toString = () => label.text // Date component uses string labels\n }\n\n if (hasError || errorMessage) {\n classes = `${classes} govuk-input--error`.trim()\n }\n\n // Allow any `toString()`-able value so non-numeric\n // values are shown alongside their error messages\n if (!isFormValue(value)) {\n value = undefined\n }\n\n return {\n label,\n id: model.id,\n name: model.name,\n type,\n value,\n classes\n }\n })\n\n fieldset ??= {\n legend: {\n text: label.text,\n classes: 'govuk-fieldset__legend--m'\n }\n }\n\n return {\n ...viewModel,\n fieldset,\n items\n }\n }\n\n isState(value?: FormStateValue | FormState) {\n return DatePartsField.isDateParts(value)\n }\n\n /**\n * For error preview page that shows all possible errors on a component\n */\n getAllPossibleErrors(): ErrorMessageTemplateList {\n return {\n baseErrors: [\n { type: 'required', template: messageTemplate.required },\n { type: 'dateFormat', template: messageTemplate.dateFormat },\n { type: 'dateFormatDay', template: '{{#label}} must include a day' },\n {\n type: 'dateFormatMonth',\n template: '{{#label}} must include a month'\n },\n { type: 'dateFormatYear', template: '{{#label}} must include a year' }\n ],\n advancedSettingsErrors: [\n { type: 'dateMin', template: messageTemplate.dateMin },\n { type: 'dateMax', template: messageTemplate.dateMax }\n ]\n }\n }\n\n static isDateParts(\n value?: FormStateValue | FormState\n ): value is DatePartsState {\n return (\n isFormState(value) &&\n NumberField.isNumber(value.day) &&\n NumberField.isNumber(value.month) &&\n NumberField.isNumber(value.year)\n )\n }\n}\n\nexport interface DatePartsState extends Record<string, number> {\n day: number\n month: number\n year: number\n}\n\nexport function getValidatorDate(component: DatePartsField) {\n const validator: CustomValidator = (payload: FormPayload, helpers) => {\n const { collection, name, options } = component\n\n const values = component.getFormValueFromState(\n component.getStateFromValidForm(payload)\n )\n\n const context: Context = {\n missing: collection.keys,\n key: name\n }\n\n if (!component.isState(values)) {\n return options.required !== false\n ? helpers.error('object.required', context)\n : payload\n }\n\n const date = parse(\n `${values.year}-${values.month}-${values.day}`,\n 'yyyy-MM-dd',\n new Date()\n )\n\n if (!isValid(date)) {\n return helpers.error('date.format', context)\n }\n\n // Minimum date from today\n const dateMin = options.maxDaysInPast\n ? sub(startOfToday(), { days: options.maxDaysInPast })\n : undefined\n\n // Maximum date from today\n const dateMax = options.maxDaysInFuture\n ? add(startOfToday(), { days: options.maxDaysInFuture })\n : undefined\n\n if (dateMin && date < dateMin) {\n return helpers.error('date.min', { ...context, limit: dateMin })\n }\n\n if (dateMax && date > dateMax) {\n return helpers.error('date.max', { ...context, limit: dateMax })\n }\n\n return payload\n }\n\n return validator\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import joi from 'joi';
|
|
2
2
|
import { FormComponent } from "./FormComponent.js";
|
|
3
|
+
import { messageTemplate } from "../pageControllers/validationOptions.js";
|
|
3
4
|
export class EmailAddressField extends FormComponent {
|
|
4
5
|
constructor(def, props) {
|
|
5
6
|
super(def, props);
|
|
6
7
|
const {
|
|
7
|
-
options
|
|
8
|
-
title
|
|
8
|
+
options
|
|
9
9
|
} = def;
|
|
10
|
-
let formSchema = joi.string().email().trim().label(
|
|
10
|
+
let formSchema = joi.string().email().trim().label(this.label).required();
|
|
11
11
|
if (options.required === false) {
|
|
12
12
|
formSchema = formSchema.allow('');
|
|
13
13
|
}
|
|
@@ -36,5 +36,21 @@ export class EmailAddressField extends FormComponent {
|
|
|
36
36
|
type: 'email'
|
|
37
37
|
};
|
|
38
38
|
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* For error preview page that shows all possible errors on a component
|
|
42
|
+
*/
|
|
43
|
+
getAllPossibleErrors() {
|
|
44
|
+
return {
|
|
45
|
+
baseErrors: [{
|
|
46
|
+
type: 'required',
|
|
47
|
+
template: messageTemplate.required
|
|
48
|
+
}, {
|
|
49
|
+
type: 'format',
|
|
50
|
+
template: messageTemplate.format
|
|
51
|
+
}],
|
|
52
|
+
advancedSettingsErrors: []
|
|
53
|
+
};
|
|
54
|
+
}
|
|
39
55
|
}
|
|
40
56
|
//# sourceMappingURL=EmailAddressField.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EmailAddressField.js","names":["joi","FormComponent","EmailAddressField","constructor","def","props","options","
|
|
1
|
+
{"version":3,"file":"EmailAddressField.js","names":["joi","FormComponent","messageTemplate","EmailAddressField","constructor","def","props","options","formSchema","string","email","trim","label","required","allow","customValidationMessage","message","messages","customValidationMessages","default","stateSchema","getViewModel","payload","errors","viewModel","attributes","autocomplete","type","getAllPossibleErrors","baseErrors","template","format","advancedSettingsErrors"],"sources":["../../../../../src/server/plugins/engine/components/EmailAddressField.ts"],"sourcesContent":["import { type EmailAddressFieldComponent } from '@defra/forms-model'\nimport joi from 'joi'\n\nimport { FormComponent } from '~/src/server/plugins/engine/components/FormComponent.js'\nimport { messageTemplate } from '~/src/server/plugins/engine/pageControllers/validationOptions.js'\nimport {\n type ErrorMessageTemplateList,\n type FormPayload,\n type FormSubmissionError\n} from '~/src/server/plugins/engine/types.js'\n\nexport class EmailAddressField extends FormComponent {\n declare options: EmailAddressFieldComponent['options']\n\n constructor(\n def: EmailAddressFieldComponent,\n props: ConstructorParameters<typeof FormComponent>[1]\n ) {\n super(def, props)\n\n const { options } = def\n\n let formSchema = joi.string().email().trim().label(this.label).required()\n\n if (options.required === false) {\n formSchema = formSchema.allow('')\n }\n\n if (options.customValidationMessage) {\n const message = options.customValidationMessage\n\n formSchema = formSchema.messages({\n 'any.required': message,\n 'string.empty': message,\n 'string.email': message\n })\n } else if (options.customValidationMessages) {\n formSchema = formSchema.messages(options.customValidationMessages)\n }\n\n this.formSchema = formSchema.default('')\n this.stateSchema = formSchema.default(null).allow(null)\n this.options = options\n }\n\n getViewModel(payload: FormPayload, errors?: FormSubmissionError[]) {\n const viewModel = super.getViewModel(payload, errors)\n const { attributes } = viewModel\n\n attributes.autocomplete = 'email'\n\n return {\n ...viewModel,\n type: 'email'\n }\n }\n\n /**\n * For error preview page that shows all possible errors on a component\n */\n getAllPossibleErrors(): ErrorMessageTemplateList {\n return {\n baseErrors: [\n { type: 'required', template: messageTemplate.required },\n { type: 'format', template: messageTemplate.format }\n ],\n advancedSettingsErrors: []\n }\n }\n}\n"],"mappings":"AACA,OAAOA,GAAG,MAAM,KAAK;AAErB,SAASC,aAAa;AACtB,SAASC,eAAe;AAOxB,OAAO,MAAMC,iBAAiB,SAASF,aAAa,CAAC;EAGnDG,WAAWA,CACTC,GAA+B,EAC/BC,KAAqD,EACrD;IACA,KAAK,CAACD,GAAG,EAAEC,KAAK,CAAC;IAEjB,MAAM;MAAEC;IAAQ,CAAC,GAAGF,GAAG;IAEvB,IAAIG,UAAU,GAAGR,GAAG,CAACS,MAAM,CAAC,CAAC,CAACC,KAAK,CAAC,CAAC,CAACC,IAAI,CAAC,CAAC,CAACC,KAAK,CAAC,IAAI,CAACA,KAAK,CAAC,CAACC,QAAQ,CAAC,CAAC;IAEzE,IAAIN,OAAO,CAACM,QAAQ,KAAK,KAAK,EAAE;MAC9BL,UAAU,GAAGA,UAAU,CAACM,KAAK,CAAC,EAAE,CAAC;IACnC;IAEA,IAAIP,OAAO,CAACQ,uBAAuB,EAAE;MACnC,MAAMC,OAAO,GAAGT,OAAO,CAACQ,uBAAuB;MAE/CP,UAAU,GAAGA,UAAU,CAACS,QAAQ,CAAC;QAC/B,cAAc,EAAED,OAAO;QACvB,cAAc,EAAEA,OAAO;QACvB,cAAc,EAAEA;MAClB,CAAC,CAAC;IACJ,CAAC,MAAM,IAAIT,OAAO,CAACW,wBAAwB,EAAE;MAC3CV,UAAU,GAAGA,UAAU,CAACS,QAAQ,CAACV,OAAO,CAACW,wBAAwB,CAAC;IACpE;IAEA,IAAI,CAACV,UAAU,GAAGA,UAAU,CAACW,OAAO,CAAC,EAAE,CAAC;IACxC,IAAI,CAACC,WAAW,GAAGZ,UAAU,CAACW,OAAO,CAAC,IAAI,CAAC,CAACL,KAAK,CAAC,IAAI,CAAC;IACvD,IAAI,CAACP,OAAO,GAAGA,OAAO;EACxB;EAEAc,YAAYA,CAACC,OAAoB,EAAEC,MAA8B,EAAE;IACjE,MAAMC,SAAS,GAAG,KAAK,CAACH,YAAY,CAACC,OAAO,EAAEC,MAAM,CAAC;IACrD,MAAM;MAAEE;IAAW,CAAC,GAAGD,SAAS;IAEhCC,UAAU,CAACC,YAAY,GAAG,OAAO;IAEjC,OAAO;MACL,GAAGF,SAAS;MACZG,IAAI,EAAE;IACR,CAAC;EACH;;EAEA;AACF;AACA;EACEC,oBAAoBA,CAAA,EAA6B;IAC/C,OAAO;MACLC,UAAU,EAAE,CACV;QAAEF,IAAI,EAAE,UAAU;QAAEG,QAAQ,EAAE5B,eAAe,CAACW;MAAS,CAAC,EACxD;QAAEc,IAAI,EAAE,QAAQ;QAAEG,QAAQ,EAAE5B,eAAe,CAAC6B;MAAO,CAAC,CACrD;MACDC,sBAAsB,EAAE;IAC1B,CAAC;EACH;AACF","ignoreList":[]}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import joi from 'joi';
|
|
2
2
|
import { FormComponent, isUploadState } from "./FormComponent.js";
|
|
3
|
+
import { messageTemplate } from "../pageControllers/validationOptions.js";
|
|
3
4
|
import { FileStatus, UploadStatus } from "../types.js";
|
|
4
5
|
import { render } from "../../nunjucks/index.js";
|
|
5
6
|
export const uploadIdSchema = joi.string().uuid().required();
|
|
@@ -48,10 +49,9 @@ export class FileUploadField extends FormComponent {
|
|
|
48
49
|
super(def, props);
|
|
49
50
|
const {
|
|
50
51
|
options,
|
|
51
|
-
schema
|
|
52
|
-
title
|
|
52
|
+
schema
|
|
53
53
|
} = def;
|
|
54
|
-
let formSchema = joi.array().label(
|
|
54
|
+
let formSchema = joi.array().label(this.label).single().required();
|
|
55
55
|
if (options.required === false) {
|
|
56
56
|
formSchema = formSchema.optional();
|
|
57
57
|
}
|
|
@@ -169,7 +169,7 @@ export class FileUploadField extends FormComponent {
|
|
|
169
169
|
});
|
|
170
170
|
|
|
171
171
|
// Set up the `accept` attribute
|
|
172
|
-
if ('accept' in options) {
|
|
172
|
+
if ('accept' in options && options.accept) {
|
|
173
173
|
attributes.accept = options.accept;
|
|
174
174
|
}
|
|
175
175
|
const summaryList = {
|
|
@@ -191,5 +191,45 @@ export class FileUploadField extends FormComponent {
|
|
|
191
191
|
isValue(value) {
|
|
192
192
|
return isUploadState(value);
|
|
193
193
|
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* For error preview page that shows all possible errors on a component
|
|
197
|
+
*/
|
|
198
|
+
getAllPossibleErrors() {
|
|
199
|
+
return {
|
|
200
|
+
baseErrors: [{
|
|
201
|
+
type: 'selectRequired',
|
|
202
|
+
template: messageTemplate.selectRequired
|
|
203
|
+
}, {
|
|
204
|
+
type: 'filesMimes',
|
|
205
|
+
template: 'The selected file must be a {{#limit}}'
|
|
206
|
+
}, {
|
|
207
|
+
type: 'filesSize',
|
|
208
|
+
template: 'The selected file must be smaller than 100MB'
|
|
209
|
+
}, {
|
|
210
|
+
type: 'filesEmpty',
|
|
211
|
+
template: 'The selected file is empty'
|
|
212
|
+
}, {
|
|
213
|
+
type: 'filesVirus',
|
|
214
|
+
template: 'The selected file contains a virus'
|
|
215
|
+
}, {
|
|
216
|
+
type: 'filesPartial',
|
|
217
|
+
template: 'The selected file has not fully uploaded'
|
|
218
|
+
}, {
|
|
219
|
+
type: 'filesError',
|
|
220
|
+
template: 'The selected file could not be uploaded – try again'
|
|
221
|
+
}],
|
|
222
|
+
advancedSettingsErrors: [{
|
|
223
|
+
type: 'filesMin',
|
|
224
|
+
template: 'You must upload {{#limit}} files or more'
|
|
225
|
+
}, {
|
|
226
|
+
type: 'filesMax',
|
|
227
|
+
template: 'You can only upload {{#limit}} files or less'
|
|
228
|
+
}, {
|
|
229
|
+
type: 'filesExact',
|
|
230
|
+
template: 'You must upload exactly {{#limit}} files'
|
|
231
|
+
}]
|
|
232
|
+
};
|
|
233
|
+
}
|
|
194
234
|
}
|
|
195
235
|
//# sourceMappingURL=FileUploadField.js.map
|