@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":"FileUploadField.js","names":["joi","FormComponent","isUploadState","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","title","formSchema","array","label","single","length","max","min","items","stateSchema","default","allow","getFormValueFromState","state","name","getFormValue","value","isValue","undefined","getDisplayStringFromState","files","unit","getContextValueFromState","map","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"],"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 {\n FileStatus,\n UploadStatus,\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, title } = def\n\n let formSchema = joi.array<FileState>().label(title).single().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 getDisplayStringFromState(state: FormSubmissionState) {\n const files = this.getFormValueFromState(state)\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 getContextValueFromState(state: FormSubmissionState) {\n const files = this.getFormValueFromState(state)\n return files?.map(({ status }) => status.form.file.fileId) ?? null\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) {\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"],"mappings":"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","ignoreList":[]}
|
|
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","getDisplayStringFromState","files","unit","getContextValueFromState","map","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 getDisplayStringFromState(state: FormSubmissionState) {\n const files = this.getFormValueFromState(state)\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 getContextValueFromState(state: FormSubmissionState) {\n const files = this.getFormValueFromState(state)\n return files?.map(({ status }) => status.form.file.fileId) ?? null\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 {\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":[]}
|
|
@@ -3,6 +3,7 @@ import { optionalText } from "./constants.js";
|
|
|
3
3
|
export class FormComponent extends ComponentBase {
|
|
4
4
|
type;
|
|
5
5
|
hint;
|
|
6
|
+
label;
|
|
6
7
|
isFormComponent = true;
|
|
7
8
|
constructor(def, props) {
|
|
8
9
|
super(def, props);
|
|
@@ -12,6 +13,7 @@ export class FormComponent extends ComponentBase {
|
|
|
12
13
|
} = def;
|
|
13
14
|
this.type = type;
|
|
14
15
|
this.hint = hint;
|
|
16
|
+
this.label = 'shortDescription' in def && def.shortDescription ? def.shortDescription : def.title;
|
|
15
17
|
}
|
|
16
18
|
get keys() {
|
|
17
19
|
const {
|
|
@@ -77,9 +79,13 @@ export class FormComponent extends ComponentBase {
|
|
|
77
79
|
}
|
|
78
80
|
return list;
|
|
79
81
|
}
|
|
80
|
-
|
|
82
|
+
getFirstError(errors) {
|
|
81
83
|
return this.getErrors(errors)?.[0];
|
|
82
84
|
}
|
|
85
|
+
getViewErrors(errors) {
|
|
86
|
+
const firstError = this.getFirstError(errors);
|
|
87
|
+
return firstError && [firstError];
|
|
88
|
+
}
|
|
83
89
|
getViewModel(payload, errors) {
|
|
84
90
|
const {
|
|
85
91
|
hint,
|
|
@@ -99,7 +105,7 @@ export class FormComponent extends ComponentBase {
|
|
|
99
105
|
|
|
100
106
|
// Filter component errors only
|
|
101
107
|
const componentErrors = this.getErrors(errors);
|
|
102
|
-
const componentError = this.
|
|
108
|
+
const componentError = this.getFirstError(componentErrors);
|
|
103
109
|
if (componentErrors) {
|
|
104
110
|
viewModel.errors = componentErrors;
|
|
105
111
|
}
|
|
@@ -143,6 +149,12 @@ export class FormComponent extends ComponentBase {
|
|
|
143
149
|
isState(value) {
|
|
144
150
|
return isFormState(value);
|
|
145
151
|
}
|
|
152
|
+
getAllPossibleErrors() {
|
|
153
|
+
return {
|
|
154
|
+
baseErrors: [],
|
|
155
|
+
advancedSettingsErrors: []
|
|
156
|
+
};
|
|
157
|
+
}
|
|
146
158
|
}
|
|
147
159
|
|
|
148
160
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FormComponent.js","names":["ComponentBase","optionalText","FormComponent","type","hint","isFormComponent","constructor","def","props","keys","collection","name","fields","map","getFormDataFromState","state","getFormValue","getFormValueFromState","value","isValue","undefined","getStateFromValidForm","payload","getErrors","errors","list","filter","error","path","includes","length","getError","getViewModel","options","title","viewModel","isRequired","required","hideOptional","label","text","componentErrors","componentError","errorMessage","id","getDisplayStringFromState","toString","getContextValueFromState","isState","values","Object","isFormValue","Array","isArray","isFormState","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 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\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 }\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 getError(errors?: FormSubmissionError[]): FormSubmissionError | undefined {\n return this.getErrors(errors)?.[0]\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.getError(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 getDisplayStringFromState(state: FormSubmissionState): string {\n const value = this.getFormValueFromState(state)\n return this.isValue(value) ? value.toString() : ''\n }\n\n getContextValueFromState(\n state: FormSubmissionState\n ): Item['value'] | Item['value'][] | null {\n const value = this.getFormValueFromState(state)\n\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 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/**\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","getDisplayStringFromState","toString","getContextValueFromState","isState","values","Object","isFormValue","Array","isArray","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 getDisplayStringFromState(state: FormSubmissionState): string {\n const value = this.getFormValueFromState(state)\n return this.isValue(value) ? value.toString() : ''\n }\n\n getContextValueFromState(\n state: FormSubmissionState\n ): Item['value'] | Item['value'][] | null {\n const value = this.getFormValueFromState(state)\n\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 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 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,5 +1,6 @@
|
|
|
1
1
|
import joi from 'joi';
|
|
2
2
|
import { FormComponent } from "./FormComponent.js";
|
|
3
|
+
import { messageTemplate } from "../pageControllers/validationOptions.js";
|
|
3
4
|
export class ListFormComponent extends FormComponent {
|
|
4
5
|
list;
|
|
5
6
|
listType = 'string';
|
|
@@ -14,8 +15,7 @@ export class ListFormComponent extends FormComponent {
|
|
|
14
15
|
constructor(def, props) {
|
|
15
16
|
super(def, props);
|
|
16
17
|
const {
|
|
17
|
-
options
|
|
18
|
-
title
|
|
18
|
+
options
|
|
19
19
|
} = def;
|
|
20
20
|
const {
|
|
21
21
|
model
|
|
@@ -24,7 +24,7 @@ export class ListFormComponent extends FormComponent {
|
|
|
24
24
|
this.list = model.getList(def.list);
|
|
25
25
|
this.listType = this.list?.type ?? 'string';
|
|
26
26
|
}
|
|
27
|
-
let formSchema = joi[this.listType]().valid(...this.values).label(
|
|
27
|
+
let formSchema = joi[this.listType]().valid(...this.values).label(this.label).required();
|
|
28
28
|
if (options.customValidationMessages) {
|
|
29
29
|
formSchema = formSchema.messages(options.customValidationMessages);
|
|
30
30
|
}
|
|
@@ -83,5 +83,18 @@ export class ListFormComponent extends FormComponent {
|
|
|
83
83
|
items
|
|
84
84
|
};
|
|
85
85
|
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* For error preview page that shows all possible errors on a component
|
|
89
|
+
*/
|
|
90
|
+
getAllPossibleErrors() {
|
|
91
|
+
return {
|
|
92
|
+
baseErrors: [{
|
|
93
|
+
type: 'selectRequired',
|
|
94
|
+
template: messageTemplate.selectRequired
|
|
95
|
+
}],
|
|
96
|
+
advancedSettingsErrors: []
|
|
97
|
+
};
|
|
98
|
+
}
|
|
86
99
|
}
|
|
87
100
|
//# sourceMappingURL=ListFormComponent.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ListFormComponent.js","names":["joi","FormComponent","ListFormComponent","list","listType","items","values","map","value","constructor","def","props","options","
|
|
1
|
+
{"version":3,"file":"ListFormComponent.js","names":["joi","FormComponent","messageTemplate","ListFormComponent","list","listType","items","values","map","value","constructor","def","props","options","model","getList","type","formSchema","valid","label","required","customValidationMessages","messages","stateSchema","default","allow","getFormValueFromState","state","name","isValue","flat","selected","filter","item","includes","at","getDisplayStringFromState","text","join","getViewModel","payload","errors","listItems","viewModel","itemModel","description","hint","getAllPossibleErrors","baseErrors","template","selectRequired","advancedSettingsErrors"],"sources":["../../../../../src/server/plugins/engine/components/ListFormComponent.ts"],"sourcesContent":["import {\n type Item,\n type List,\n type ListComponentsDef,\n type SelectionComponentsDef,\n type YesNoFieldComponent\n} from '@defra/forms-model'\nimport joi, {\n type ArraySchema,\n type BooleanSchema,\n type NumberSchema,\n type StringSchema\n} from 'joi'\n\nimport { FormComponent } from '~/src/server/plugins/engine/components/FormComponent.js'\nimport { type ListItem } 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 FormSubmissionError,\n type FormSubmissionState\n} from '~/src/server/plugins/engine/types.js'\n\nexport class ListFormComponent extends FormComponent {\n declare options: Extract<\n SelectionComponentsDef,\n { options: object }\n >['options']\n\n declare formSchema:\n | ArraySchema<string>\n | ArraySchema<number>\n | BooleanSchema<string>\n | NumberSchema<string>\n | NumberSchema\n | StringSchema\n\n declare stateSchema:\n | ArraySchema<string>\n | ArraySchema<number>\n | BooleanSchema<string>\n | NumberSchema<string>\n | NumberSchema\n | StringSchema\n\n list?: List\n listType: List['type'] = 'string'\n\n get items(): Item[] {\n return this.list?.items ?? []\n }\n\n get values(): Item['value'][] {\n return this.items.map(({ value }) => value)\n }\n\n constructor(\n def:\n | SelectionComponentsDef // Allow for Yes/No field custom list\n | (YesNoFieldComponent & Pick<ListComponentsDef, 'list'>),\n props: ConstructorParameters<typeof FormComponent>[1]\n ) {\n super(def, props)\n\n const { options } = def\n const { model } = props\n\n if ('list' in def) {\n this.list = model.getList(def.list)\n this.listType = this.list?.type ?? 'string'\n }\n\n let formSchema = joi[this.listType]()\n .valid(...this.values)\n .label(this.label)\n .required()\n\n if (options.customValidationMessages) {\n formSchema = formSchema.messages(options.customValidationMessages)\n }\n\n this.formSchema = formSchema\n this.stateSchema = formSchema.default(null).allow(null)\n this.options = options\n }\n\n getFormValueFromState(\n state: FormSubmissionState\n ): Item['value'] | Item['value'][] | undefined {\n const { name, items } = this\n\n const value = state[name]\n\n // Allow for array values via subclass\n const values = this.isValue(value) ? [value].flat() : []\n const selected = items.filter((item) => values.includes(item.value))\n\n return selected.at(0)?.value\n }\n\n getDisplayStringFromState(state: FormSubmissionState) {\n const { items } = this\n\n // Allow for array values via subclass\n const value = this.getFormValueFromState(state)\n const values = [value ?? []].flat()\n\n return items\n .filter((item) => values.includes(item.value))\n .map((item) => item.text)\n .join(', ')\n }\n\n getViewModel(payload: FormPayload, errors?: FormSubmissionError[]) {\n const { items: listItems } = this\n\n const viewModel = super.getViewModel(payload, errors)\n const { value } = viewModel\n\n // Support multiple values for checkboxes\n const values = this.isValue(value) ? [value].flat() : []\n\n const items = listItems.map((item) => {\n const selected = values.includes(item.value)\n const itemModel: ListItem = { ...item, selected }\n\n if (item.description) {\n itemModel.hint = {\n text: item.description\n }\n }\n\n return itemModel\n })\n\n return {\n ...viewModel,\n items\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: 'selectRequired', template: messageTemplate.selectRequired }\n ],\n advancedSettingsErrors: []\n }\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { ComponentBase } from "./ComponentBase.js";
|
|
2
|
+
export class Markdown extends ComponentBase {
|
|
3
|
+
content;
|
|
4
|
+
constructor(def, props) {
|
|
5
|
+
super(def, props);
|
|
6
|
+
const {
|
|
7
|
+
content,
|
|
8
|
+
options
|
|
9
|
+
} = def;
|
|
10
|
+
this.content = content;
|
|
11
|
+
this.options = options;
|
|
12
|
+
}
|
|
13
|
+
getViewModel() {
|
|
14
|
+
const {
|
|
15
|
+
content,
|
|
16
|
+
viewModel
|
|
17
|
+
} = this;
|
|
18
|
+
return {
|
|
19
|
+
...viewModel,
|
|
20
|
+
content
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=Markdown.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Markdown.js","names":["ComponentBase","Markdown","content","constructor","def","props","options","getViewModel","viewModel"],"sources":["../../../../../src/server/plugins/engine/components/Markdown.ts"],"sourcesContent":["import { type MarkdownComponent } from '@defra/forms-model'\n\nimport { ComponentBase } from '~/src/server/plugins/engine/components/ComponentBase.js'\n\nexport class Markdown extends ComponentBase {\n declare options: MarkdownComponent['options']\n content: MarkdownComponent['content']\n\n constructor(\n def: MarkdownComponent,\n props: ConstructorParameters<typeof ComponentBase>[1]\n ) {\n super(def, props)\n\n const { content, options } = def\n\n this.content = content\n this.options = options\n }\n\n getViewModel() {\n const { content, viewModel } = this\n\n return {\n ...viewModel,\n content\n }\n }\n}\n"],"mappings":"AAEA,SAASA,aAAa;AAEtB,OAAO,MAAMC,QAAQ,SAASD,aAAa,CAAC;EAE1CE,OAAO;EAEPC,WAAWA,CACTC,GAAsB,EACtBC,KAAqD,EACrD;IACA,KAAK,CAACD,GAAG,EAAEC,KAAK,CAAC;IAEjB,MAAM;MAAEH,OAAO;MAAEI;IAAQ,CAAC,GAAGF,GAAG;IAEhC,IAAI,CAACF,OAAO,GAAGA,OAAO;IACtB,IAAI,CAACI,OAAO,GAAGA,OAAO;EACxB;EAEAC,YAAYA,CAAA,EAAG;IACb,MAAM;MAAEL,OAAO;MAAEM;IAAU,CAAC,GAAG,IAAI;IAEnC,OAAO;MACL,GAAGA,SAAS;MACZN;IACF,CAAC;EACH;AACF","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 MonthYearField extends FormComponent {
|
|
8
9
|
constructor(def, props) {
|
|
9
10
|
super(def, props);
|
|
@@ -12,15 +13,17 @@ export class MonthYearField 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}__month`,
|
|
@@ -146,6 +149,31 @@ export class MonthYearField extends FormComponent {
|
|
|
146
149
|
isState(value) {
|
|
147
150
|
return MonthYearField.isMonthYear(value);
|
|
148
151
|
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* For error preview page that shows all possible errors on a component
|
|
155
|
+
*/
|
|
156
|
+
getAllPossibleErrors() {
|
|
157
|
+
return {
|
|
158
|
+
baseErrors: [{
|
|
159
|
+
type: 'required',
|
|
160
|
+
template: messageTemplate.required
|
|
161
|
+
}, {
|
|
162
|
+
type: 'dateFormatMonth',
|
|
163
|
+
template: '{{#label}} must include a month'
|
|
164
|
+
}, {
|
|
165
|
+
type: 'dateFormatYear',
|
|
166
|
+
template: '{{#label}} must include a year'
|
|
167
|
+
}],
|
|
168
|
+
advancedSettingsErrors: [{
|
|
169
|
+
type: 'dateMin',
|
|
170
|
+
template: messageTemplate.dateMin
|
|
171
|
+
}, {
|
|
172
|
+
type: 'dateMax',
|
|
173
|
+
template: messageTemplate.dateMax
|
|
174
|
+
}]
|
|
175
|
+
};
|
|
176
|
+
}
|
|
149
177
|
static isMonthYear(value) {
|
|
150
178
|
return isFormState(value) && NumberField.isNumber(value.month) && NumberField.isNumber(value.year);
|
|
151
179
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MonthYearField.js","names":["ComponentType","format","isValid","parse","ComponentCollection","FormComponent","isFormState","isFormValue","NumberField","messageTemplate","MonthYearField","constructor","def","props","name","options","isRequired","required","customValidationMessages","objectMissing","dateFormat","collection","type","title","schema","min","max","precision","optionalText","classes","parent","custom","getValidatorMonthYear","peers","formSchema","stateSchema","getFormValueFromState","state","value","isMonthYear","undefined","getDisplayStringFromState","date","Date","setMonth","month","monthString","toLocaleString","year","getContextValueFromState","getViewModel","payload","errors","viewModel","fieldset","label","hasError","some","error","items","map","model","errorMessage","toString","text","trim","id","legend","isState","isNumber","component","validator","helpers","values","getStateFromValidForm","context","missing","keys","key"],"sources":["../../../../../src/server/plugins/engine/components/MonthYearField.ts"],"sourcesContent":["import { ComponentType, type MonthYearFieldComponent } from '@defra/forms-model'\nimport { format, isValid, parse } 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 MonthYearField extends FormComponent {\n declare options: MonthYearFieldComponent['options']\n declare formSchema: ObjectSchema<FormPayload>\n declare stateSchema: ObjectSchema<FormState>\n declare collection: ComponentCollection\n\n constructor(\n def: MonthYearFieldComponent,\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}__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: getValidatorMonthYear(this),\n peers: [`${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 MonthYearField.isMonthYear(value) ? value : undefined\n }\n\n getDisplayStringFromState(state: FormSubmissionState) {\n const value = this.getFormValueFromState(state)\n\n if (!value) {\n return ''\n }\n\n const date = new Date()\n date.setMonth(value.month - 1)\n\n const monthString = date.toLocaleString('default', { month: 'long' })\n return `${monthString} ${value.year}`\n }\n\n getContextValueFromState(state: FormSubmissionState) {\n const value = this.getFormValueFromState(state)\n\n if (\n !value ||\n !isValid(\n parse(`${value.year}-${value.month}-01`, 'yyyy-MM-dd', new Date())\n )\n ) {\n return null\n }\n\n return format(`${value.year}-${value.month}-01`, 'yyyy-MM')\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 MonthYearField.isMonthYear(value)\n }\n\n static isMonthYear(\n value?: FormStateValue | FormState\n ): value is MonthYearState {\n return (\n isFormState(value) &&\n NumberField.isNumber(value.month) &&\n NumberField.isNumber(value.year)\n )\n }\n}\n\nexport interface MonthYearState extends Record<string, number> {\n month: number\n year: number\n}\n\nexport function getValidatorMonthYear(component: MonthYearField) {\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 return payload\n }\n\n return validator\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"MonthYearField.js","names":["ComponentType","format","isValid","parse","ComponentCollection","FormComponent","isFormState","isFormValue","NumberField","messageTemplate","convertToLanguageMessages","MonthYearField","constructor","def","props","name","options","isRequired","required","customValidationMessages","objectMissing","dateFormat","collection","type","title","schema","min","max","precision","optionalText","classes","parent","custom","getValidatorMonthYear","peers","formSchema","stateSchema","getFormValueFromState","state","value","isMonthYear","undefined","getDisplayStringFromState","date","Date","setMonth","month","monthString","toLocaleString","year","getContextValueFromState","getViewModel","payload","errors","viewModel","fieldset","label","hasError","some","error","items","map","model","errorMessage","toString","text","trim","id","legend","isState","getAllPossibleErrors","baseErrors","template","advancedSettingsErrors","dateMin","dateMax","isNumber","component","validator","helpers","values","getStateFromValidForm","context","missing","keys","key"],"sources":["../../../../../src/server/plugins/engine/components/MonthYearField.ts"],"sourcesContent":["import { ComponentType, type MonthYearFieldComponent } from '@defra/forms-model'\nimport { format, isValid, parse } 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 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 MonthYearField extends FormComponent {\n declare options: MonthYearFieldComponent['options']\n declare formSchema: ObjectSchema<FormPayload>\n declare stateSchema: ObjectSchema<FormState>\n declare collection: ComponentCollection\n\n constructor(\n def: MonthYearFieldComponent,\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 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}__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: getValidatorMonthYear(this),\n peers: [`${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 MonthYearField.isMonthYear(value) ? value : undefined\n }\n\n getDisplayStringFromState(state: FormSubmissionState) {\n const value = this.getFormValueFromState(state)\n\n if (!value) {\n return ''\n }\n\n const date = new Date()\n date.setMonth(value.month - 1)\n\n const monthString = date.toLocaleString('default', { month: 'long' })\n return `${monthString} ${value.year}`\n }\n\n getContextValueFromState(state: FormSubmissionState) {\n const value = this.getFormValueFromState(state)\n\n if (\n !value ||\n !isValid(\n parse(`${value.year}-${value.month}-01`, 'yyyy-MM-dd', new Date())\n )\n ) {\n return null\n }\n\n return format(`${value.year}-${value.month}-01`, 'yyyy-MM')\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 MonthYearField.isMonthYear(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 {\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 isMonthYear(\n value?: FormStateValue | FormState\n ): value is MonthYearState {\n return (\n isFormState(value) &&\n NumberField.isNumber(value.month) &&\n NumberField.isNumber(value.year)\n )\n }\n}\n\nexport interface MonthYearState extends Record<string, number> {\n month: number\n year: number\n}\n\nexport function getValidatorMonthYear(component: MonthYearField) {\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 return payload\n }\n\n return validator\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import Joi from 'joi';
|
|
2
2
|
import { FormComponent } from "./FormComponent.js";
|
|
3
|
+
import { messageTemplate } from "../pageControllers/validationOptions.js";
|
|
3
4
|
export class MultilineTextField extends FormComponent {
|
|
4
5
|
isCharacterOrWordCount = false;
|
|
5
6
|
constructor(def, props) {
|
|
6
7
|
super(def, props);
|
|
7
8
|
const {
|
|
8
9
|
schema,
|
|
9
|
-
options
|
|
10
|
-
title
|
|
10
|
+
options
|
|
11
11
|
} = def;
|
|
12
|
-
let formSchema = Joi.string().trim().label(
|
|
12
|
+
let formSchema = Joi.string().trim().label(this.label).required();
|
|
13
13
|
if (options.required === false) {
|
|
14
14
|
formSchema = formSchema.allow('');
|
|
15
15
|
}
|
|
@@ -45,6 +45,9 @@ export class MultilineTextField extends FormComponent {
|
|
|
45
45
|
});
|
|
46
46
|
} else if (options.customValidationMessages) {
|
|
47
47
|
formSchema = formSchema.messages(options.customValidationMessages);
|
|
48
|
+
} else if (typeof schema.max === 'number' && typeof schema.min === 'number') {
|
|
49
|
+
const minMaxErrorText = this.buildMinMaxText(schema.min, schema.max);
|
|
50
|
+
formSchema = formSchema.ruleset.min(schema.min).max(schema.max).message(minMaxErrorText);
|
|
48
51
|
}
|
|
49
52
|
this.formSchema = formSchema.default('');
|
|
50
53
|
this.stateSchema = formSchema.default(null).allow(null);
|
|
@@ -80,6 +83,32 @@ export class MultilineTextField extends FormComponent {
|
|
|
80
83
|
rows
|
|
81
84
|
};
|
|
82
85
|
}
|
|
86
|
+
buildMinMaxText(min, max) {
|
|
87
|
+
const minMaxError = messageTemplate.minMax;
|
|
88
|
+
return minMaxError.replace('{{#min}}', min ? min.toString() : '[min length]').replace('{{#max}}', max ? max.toString() : '[max length]');
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* For error preview page that shows all possible errors on a component
|
|
93
|
+
*/
|
|
94
|
+
getAllPossibleErrors() {
|
|
95
|
+
return {
|
|
96
|
+
baseErrors: [{
|
|
97
|
+
type: 'required',
|
|
98
|
+
template: messageTemplate.required
|
|
99
|
+
}],
|
|
100
|
+
advancedSettingsErrors: [{
|
|
101
|
+
type: 'min',
|
|
102
|
+
template: messageTemplate.min
|
|
103
|
+
}, {
|
|
104
|
+
type: 'max',
|
|
105
|
+
template: messageTemplate.max
|
|
106
|
+
}, {
|
|
107
|
+
type: 'minMax',
|
|
108
|
+
template: this.buildMinMaxText(this.schema.min, this.schema.max)
|
|
109
|
+
}]
|
|
110
|
+
};
|
|
111
|
+
}
|
|
83
112
|
}
|
|
84
113
|
function getValidatorMaxWords(component) {
|
|
85
114
|
const validator = (value, helpers) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MultilineTextField.js","names":["Joi","FormComponent","MultilineTextField","isCharacterOrWordCount","constructor","def","props","schema","options","title","formSchema","string","trim","label","required","allow","length","max","min","maxWords","custom","getValidatorMaxWords","regex","pattern","RegExp","customValidationMessage","message","messages","customValidationMessages","default","stateSchema","getViewModel","payload","errors","viewModel","maxlength","maxwords","rows","component","validator","value","helpers","limit","count","error","text","tokens","match"],"sources":["../../../../../src/server/plugins/engine/components/MultilineTextField.ts"],"sourcesContent":["import { type MultilineTextFieldComponent } from '@defra/forms-model'\nimport Joi, { type CustomValidator, type StringSchema } from 'joi'\n\nimport { type ComponentBase } from '~/src/server/plugins/engine/components/ComponentBase.js'\nimport { FormComponent } from '~/src/server/plugins/engine/components/FormComponent.js'\nimport {\n type FormPayload,\n type FormSubmissionError\n} from '~/src/server/plugins/engine/types.js'\n\nexport class MultilineTextField extends FormComponent {\n declare options: MultilineTextFieldComponent['options']\n declare schema: MultilineTextFieldComponent['schema']\n declare formSchema: StringSchema\n declare stateSchema: StringSchema\n\n isCharacterOrWordCount = false\n\n constructor(\n def: MultilineTextFieldComponent,\n props: ConstructorParameters<typeof ComponentBase>[1]\n ) {\n super(def, props)\n\n const { schema, options, title } = def\n\n let formSchema = Joi.string().trim().label(title).required()\n\n if (options.required === false) {\n formSchema = formSchema.allow('')\n }\n\n if (typeof schema.length !== 'number') {\n if (typeof schema.max === 'number') {\n formSchema = formSchema.max(schema.max)\n this.isCharacterOrWordCount = true\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 if (typeof options.maxWords === 'number') {\n formSchema = formSchema.custom(\n getValidatorMaxWords(this),\n 'max words validation'\n )\n\n this.isCharacterOrWordCount = true\n }\n\n if (schema.regex) {\n const pattern = new RegExp(schema.regex)\n formSchema = formSchema.pattern(pattern)\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.max': message,\n 'string.min': message,\n 'string.length': message,\n 'string.pattern.base': message,\n 'string.maxWords': 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 this.schema = schema\n }\n\n getViewModel(payload: FormPayload, errors?: FormSubmissionError[]) {\n const { schema, options, isCharacterOrWordCount } = this\n\n const viewModel = super.getViewModel(payload, errors)\n let { maxlength, maxwords, rows } = viewModel\n\n if (schema.max) {\n maxlength = schema.max\n }\n\n if (options.maxWords) {\n maxwords = options.maxWords\n }\n\n if (options.rows) {\n rows = options.rows\n }\n\n return {\n ...viewModel,\n isCharacterOrWordCount,\n maxlength,\n maxwords,\n rows\n }\n }\n}\n\nfunction getValidatorMaxWords(component: MultilineTextField) {\n const validator: CustomValidator = (value: string, helpers) => {\n const { options } = component\n\n const {\n customValidationMessage: custom,\n maxWords: limit // See {{#limit}} variable\n } = options\n\n if (!limit || count(value) <= limit) {\n return value\n }\n\n return custom\n ? helpers.message({ custom }, { limit })\n : helpers.error('string.maxWords', { limit })\n }\n\n /**\n * Count the number of words in the given text\n * @see GOV.UK Frontend {@link https://github.com/alphagov/govuk-frontend/blob/v5.4.0/packages/govuk-frontend/src/govuk/components/character-count/character-count.mjs#L343 | Character count `maxwords` implementation}\n */\n function count(text: string) {\n const tokens = text.match(/\\S+/g) ?? []\n return tokens.length\n }\n\n return validator\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"MultilineTextField.js","names":["Joi","FormComponent","messageTemplate","MultilineTextField","isCharacterOrWordCount","constructor","def","props","schema","options","formSchema","string","trim","label","required","allow","length","max","min","maxWords","custom","getValidatorMaxWords","regex","pattern","RegExp","customValidationMessage","message","messages","customValidationMessages","minMaxErrorText","buildMinMaxText","ruleset","default","stateSchema","getViewModel","payload","errors","viewModel","maxlength","maxwords","rows","minMaxError","minMax","replace","toString","getAllPossibleErrors","baseErrors","type","template","advancedSettingsErrors","component","validator","value","helpers","limit","count","error","text","tokens","match"],"sources":["../../../../../src/server/plugins/engine/components/MultilineTextField.ts"],"sourcesContent":["import { type MultilineTextFieldComponent } from '@defra/forms-model'\nimport Joi, { type CustomValidator, type StringSchema } from 'joi'\n\nimport { type ComponentBase } from '~/src/server/plugins/engine/components/ComponentBase.js'\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 MultilineTextField extends FormComponent {\n declare options: MultilineTextFieldComponent['options']\n declare schema: MultilineTextFieldComponent['schema']\n declare formSchema: StringSchema\n declare stateSchema: StringSchema\n\n isCharacterOrWordCount = false\n\n constructor(\n def: MultilineTextFieldComponent,\n props: ConstructorParameters<typeof ComponentBase>[1]\n ) {\n super(def, props)\n\n const { schema, options } = def\n\n let formSchema = Joi.string().trim().label(this.label).required()\n\n if (options.required === false) {\n formSchema = formSchema.allow('')\n }\n\n if (typeof schema.length !== 'number') {\n if (typeof schema.max === 'number') {\n formSchema = formSchema.max(schema.max)\n this.isCharacterOrWordCount = true\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 if (typeof options.maxWords === 'number') {\n formSchema = formSchema.custom(\n getValidatorMaxWords(this),\n 'max words validation'\n )\n\n this.isCharacterOrWordCount = true\n }\n\n if (schema.regex) {\n const pattern = new RegExp(schema.regex)\n formSchema = formSchema.pattern(pattern)\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.max': message,\n 'string.min': message,\n 'string.length': message,\n 'string.pattern.base': message,\n 'string.maxWords': message\n })\n } else if (options.customValidationMessages) {\n formSchema = formSchema.messages(options.customValidationMessages)\n } else if (\n typeof schema.max === 'number' &&\n typeof schema.min === 'number'\n ) {\n const minMaxErrorText = this.buildMinMaxText(schema.min, schema.max)\n formSchema = formSchema.ruleset\n .min(schema.min)\n .max(schema.max)\n .message(minMaxErrorText)\n }\n\n this.formSchema = formSchema.default('')\n this.stateSchema = formSchema.default(null).allow(null)\n this.options = options\n this.schema = schema\n }\n\n getViewModel(payload: FormPayload, errors?: FormSubmissionError[]) {\n const { schema, options, isCharacterOrWordCount } = this\n\n const viewModel = super.getViewModel(payload, errors)\n let { maxlength, maxwords, rows } = viewModel\n\n if (schema.max) {\n maxlength = schema.max\n }\n\n if (options.maxWords) {\n maxwords = options.maxWords\n }\n\n if (options.rows) {\n rows = options.rows\n }\n\n return {\n ...viewModel,\n isCharacterOrWordCount,\n maxlength,\n maxwords,\n rows\n }\n }\n\n buildMinMaxText(min?: number, max?: number): string {\n const minMaxError = messageTemplate.minMax as string\n return minMaxError\n .replace('{{#min}}', min ? min.toString() : '[min length]')\n .replace('{{#max}}', max ? max.toString() : '[max length]')\n }\n\n /**\n * For error preview page that shows all possible errors on a component\n */\n getAllPossibleErrors(): ErrorMessageTemplateList {\n return {\n baseErrors: [{ type: 'required', template: messageTemplate.required }],\n advancedSettingsErrors: [\n { type: 'min', template: messageTemplate.min },\n { type: 'max', template: messageTemplate.max },\n {\n type: 'minMax',\n template: this.buildMinMaxText(this.schema.min, this.schema.max)\n }\n ]\n }\n }\n}\n\nfunction getValidatorMaxWords(component: MultilineTextField) {\n const validator: CustomValidator = (value: string, helpers) => {\n const { options } = component\n\n const {\n customValidationMessage: custom,\n maxWords: limit // See {{#limit}} variable\n } = options\n\n if (!limit || count(value) <= limit) {\n return value\n }\n\n return custom\n ? helpers.message({ custom }, { limit })\n : helpers.error('string.maxWords', { limit })\n }\n\n /**\n * Count the number of words in the given text\n * @see GOV.UK Frontend {@link https://github.com/alphagov/govuk-frontend/blob/v5.4.0/packages/govuk-frontend/src/govuk/components/character-count/character-count.mjs#L343 | Character count `maxwords` implementation}\n */\n function count(text: string) {\n const tokens = text.match(/\\S+/g) ?? []\n return tokens.length\n }\n\n return validator\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -6,15 +6,15 @@ export class NumberField extends FormComponent {
|
|
|
6
6
|
super(def, props);
|
|
7
7
|
const {
|
|
8
8
|
options,
|
|
9
|
-
schema
|
|
10
|
-
title
|
|
9
|
+
schema
|
|
11
10
|
} = def;
|
|
12
|
-
let formSchema = joi.number().custom(getValidatorPrecision(this)).label(
|
|
11
|
+
let formSchema = joi.number().custom(getValidatorPrecision(this)).label(this.label).required();
|
|
13
12
|
if (options.required === false) {
|
|
14
13
|
formSchema = formSchema.allow('');
|
|
15
14
|
} else {
|
|
16
15
|
const messages = options.customValidationMessages;
|
|
17
16
|
formSchema = formSchema.empty('').messages({
|
|
17
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
18
18
|
'any.required': messages?.['any.required'] ?? messageTemplate.required
|
|
19
19
|
});
|
|
20
20
|
}
|
|
@@ -98,6 +98,31 @@ export class NumberField extends FormComponent {
|
|
|
98
98
|
isValue(value) {
|
|
99
99
|
return NumberField.isNumber(value);
|
|
100
100
|
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* For error preview page that shows all possible errors on a component
|
|
104
|
+
*/
|
|
105
|
+
getAllPossibleErrors() {
|
|
106
|
+
return {
|
|
107
|
+
baseErrors: [{
|
|
108
|
+
type: 'required',
|
|
109
|
+
template: messageTemplate.required
|
|
110
|
+
}, {
|
|
111
|
+
type: 'numberInteger',
|
|
112
|
+
template: messageTemplate.numberInteger
|
|
113
|
+
}],
|
|
114
|
+
advancedSettingsErrors: [{
|
|
115
|
+
type: 'numberMin',
|
|
116
|
+
template: messageTemplate.numberMin
|
|
117
|
+
}, {
|
|
118
|
+
type: 'numberMax',
|
|
119
|
+
template: messageTemplate.numberMax
|
|
120
|
+
}, {
|
|
121
|
+
type: 'numberPrecision',
|
|
122
|
+
template: messageTemplate.numberPrecision
|
|
123
|
+
}]
|
|
124
|
+
};
|
|
125
|
+
}
|
|
101
126
|
static isNumber(value) {
|
|
102
127
|
return typeof value === 'number';
|
|
103
128
|
}
|