@defra/forms-engine-plugin 4.6.0 → 4.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.server/server/plugins/engine/beta/form-context.js +1 -5
- package/.server/server/plugins/engine/beta/form-context.js.map +1 -1
- package/.server/server/plugins/engine/components/CheckboxesField.d.ts +10 -1
- package/.server/server/plugins/engine/components/CheckboxesField.js +39 -0
- package/.server/server/plugins/engine/components/CheckboxesField.js.map +1 -1
- package/.server/server/plugins/engine/models/FormModel.d.ts +0 -2
- package/.server/server/plugins/engine/models/FormModel.js +1 -4
- package/.server/server/plugins/engine/models/FormModel.js.map +1 -1
- package/.server/server/plugins/engine/outputFormatters/adapter/v1.d.ts +0 -4
- package/.server/server/plugins/engine/outputFormatters/adapter/v1.js +1 -22
- package/.server/server/plugins/engine/outputFormatters/adapter/v1.js.map +1 -1
- package/.server/server/plugins/engine/pageControllers/validationOptions.js +4 -1
- package/.server/server/plugins/engine/pageControllers/validationOptions.js.map +1 -1
- package/.server/server/plugins/engine/types.d.ts +0 -1
- package/.server/server/plugins/engine/types.js.map +1 -1
- package/package.json +2 -2
- package/src/server/plugins/engine/beta/form-context.test.ts +0 -2
- package/src/server/plugins/engine/beta/form-context.ts +1 -8
- package/src/server/plugins/engine/components/CheckboxesField.test.ts +56 -1
- package/src/server/plugins/engine/components/CheckboxesField.ts +40 -0
- package/src/server/plugins/engine/models/FormModel.test.ts +0 -64
- package/src/server/plugins/engine/models/FormModel.ts +1 -5
- package/src/server/plugins/engine/outputFormatters/adapter/v1.test.ts +4 -356
- package/src/server/plugins/engine/outputFormatters/adapter/v1.ts +1 -31
- package/src/server/plugins/engine/pageControllers/validationOptions.ts +4 -1
- package/src/server/plugins/engine/types.ts +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","names":["FileStatus","UploadStatus"],"sources":["../../../../src/server/plugins/engine/types.ts"],"sourcesContent":["import {\n type ComponentDef,\n type Event,\n type FormVersionMetadata,\n type Item,\n type List,\n type Page,\n type PaymentFieldComponent,\n type UkAddressFieldComponent\n} from '@defra/forms-model'\nimport {\n type PluginProperties,\n type Request,\n type ResponseObject\n} from '@hapi/hapi'\nimport { type JoiExpression, type ValidationErrorItem } from 'joi'\n\nimport { FormComponent } from '~/src/server/plugins/engine/components/FormComponent.js'\nimport { type UkAddressState } from '~/src/server/plugins/engine/components/UkAddressField.js'\nimport { type Component } from '~/src/server/plugins/engine/components/helpers/components.js'\nimport {\n type FileUploadField,\n type PaymentField\n} from '~/src/server/plugins/engine/components/index.js'\nimport {\n type BackLink,\n type ComponentText,\n type ComponentViewModel,\n type DatePartsState,\n type EastingNorthingState,\n type LatLongState,\n type MonthYearState\n} from '~/src/server/plugins/engine/components/types.js'\nimport { type FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport { type DetailItemField } from '~/src/server/plugins/engine/models/types.js'\nimport { type PageController } from '~/src/server/plugins/engine/pageControllers/PageController.js'\nimport { type PageControllerClass } from '~/src/server/plugins/engine/pageControllers/helpers/pages.js'\nimport { type QuestionPageController } from '~/src/server/plugins/engine/pageControllers/index.js'\nimport {\n type FileStatus,\n type FormAdapterSubmissionSchemaVersion,\n type UploadStatus\n} from '~/src/server/plugins/engine/types/enums.js'\nimport { type ViewContext } from '~/src/server/plugins/nunjucks/types.js'\nimport {\n type FormAction,\n type FormRequest,\n type FormRequestPayload,\n type FormResponseToolkit,\n type FormStatus\n} from '~/src/server/routes/types.js'\nimport { type CacheService } from '~/src/server/services/cacheService.js'\nimport { type RequestOptions } from '~/src/server/services/httpService.js'\nimport { type Services } from '~/src/server/types.js'\n\nexport type AnyFormRequest = FormRequest | FormRequestPayload\nexport type AnyRequest = Request | AnyFormRequest\n\n/**\n * Form submission state stores the following in Redis:\n * Props containing user's submitted values as `{ [inputId]: value }` or as `{ [sectionName]: { [inputName]: value } }`\n * a) . e.g:\n * ```ts\n * {\n * _C9PRHmsgt: 'Ben',\n * WfLk9McjzX: 'Music',\n * IK7jkUFCBL: 'Royal Academy of Music'\n * }\n * ```\n *\n * b)\n * ```ts\n * {\n * checkBeforeYouStart: { ukPassport: true },\n * applicantDetails: {\n * numberOfApplicants: 1,\n * phoneNumber: '77777777',\n * emailAddress: 'aaa@aaa.com'\n * },\n * applicantOneDetails: {\n * firstName: 'a',\n * middleName: 'a',\n * lastName: 'a',\n * address: { addressLine1: 'a', addressLine2: 'a', town: 'a', postcode: 'a' }\n * }\n * }\n * ```\n */\n\n/**\n * Form submission state\n */\nexport type FormSubmissionState = {\n upload?: Record<string, TempFileState>\n} & FormState\n\nexport interface FormSubmissionError extends Pick<\n ValidationErrorItem,\n 'context' | 'path'\n> {\n href: string // e.g: '#dateField__day'\n name: string // e.g: 'dateField__day'\n text: string // e.g: 'Date field must be a real date'\n}\n\nexport interface FormConfirmationState {\n confirmed?: true\n formId?: string\n referenceNumber?: string\n}\n\nexport interface FormPayloadParams {\n action?: FormAction\n confirm?: true\n crumb?: string\n itemId?: string\n}\n\n/**\n * Form POST for question pages\n * (after Joi has converted value types)\n */\nexport type FormPayload = FormPayloadParams & Partial<Record<string, FormValue>>\n\nexport type FormValue =\n | Item['value']\n | Item['value'][]\n | UploadState\n | RepeatListState\n | GeospatialState\n | undefined\n\nexport type FormState = Partial<Record<string, FormStateValue>>\nexport type FormStateValue = Exclude<FormValue, undefined> | null\n\nexport interface FormValidationResult<\n ValueType extends FormPayload | FormSubmissionState\n> {\n value: ValueType\n errors: FormSubmissionError[] | undefined\n}\n\nexport interface FormContext {\n /**\n * Evaluation form state only (filtered by visited paths),\n * with values formatted for condition evaluation using\n * {@link FormComponent.getContextValueFromState}\n */\n evaluationState: FormState\n\n /**\n * Relevant form state only (filtered by visited paths)\n */\n relevantState: FormState\n\n /**\n * Relevant pages only (filtered by visited paths)\n */\n relevantPages: PageControllerClass[]\n\n /**\n * Form submission payload (single page)\n */\n payload: FormPayload\n\n /**\n * Form submission state (entire form)\n */\n state: FormSubmissionState\n\n /**\n * Validation errors (entire form)\n */\n errors?: FormSubmissionError[]\n\n /**\n * Visited paths evaluated from form state\n */\n paths: string[]\n\n /**\n * Preview URL direct access is allowed\n */\n isForceAccess: boolean\n\n /**\n * Miscellaneous extra data from event responses\n */\n data: object\n\n pageDefMap: Map<string, Page>\n listDefMap: Map<string, List>\n componentDefMap: Map<string, ComponentDef>\n pageMap: Map<string, PageControllerClass>\n componentMap: Map<string, Component>\n referenceNumber: string\n submittedVersionNumber?: number\n}\n\nexport type FormContextRequest = (\n | {\n method: 'get'\n payload?: undefined\n }\n | {\n method: 'post'\n payload: FormPayload\n }\n | {\n method: FormRequest['method']\n payload?: object | undefined\n }\n) &\n Pick<\n FormRequest,\n 'app' | 'method' | 'params' | 'path' | 'query' | 'url' | 'server'\n >\n\nexport interface UploadInitiateResponse {\n uploadId: string\n uploadUrl: string\n statusUrl: string\n}\n\nexport {\n FileStatus,\n UploadStatus\n} from '~/src/server/plugins/engine/types/enums.js'\n\nexport type UploadState = FileState[]\n\nexport type FileUpload = {\n fileId: string\n filename: string\n contentLength: number\n} & (\n | {\n fileStatus: FileStatus.complete | FileStatus.rejected | FileStatus.pending\n errorMessage?: string\n }\n | {\n fileStatus: FileStatus.complete\n errorMessage?: undefined\n }\n)\n\nexport interface FileUploadMetadata {\n retrievalKey: string\n}\n\nexport type UploadStatusResponse =\n | {\n uploadStatus: UploadStatus.initiated\n metadata: FileUploadMetadata\n form: { file?: undefined }\n }\n | {\n uploadStatus: UploadStatus.pending | UploadStatus.ready\n metadata: FileUploadMetadata\n form: { file: FileUpload }\n numberOfRejectedFiles?: number\n }\n | {\n uploadStatus: UploadStatus.ready\n metadata: FileUploadMetadata\n form: { file: FileUpload }\n numberOfRejectedFiles: 0\n }\n\nexport type UploadStatusFileResponse = Exclude<\n UploadStatusResponse,\n { uploadStatus: UploadStatus.initiated }\n>\n\nexport interface FileState {\n uploadId: string\n status: UploadStatusFileResponse\n}\n\nexport interface TempFileState {\n upload?: UploadInitiateResponse\n files: UploadState\n}\n\nexport interface RepeatItemState extends FormPayload {\n itemId: string\n}\n\nexport type RepeatListState = RepeatItemState[]\n\n/**\n * A longitude/latitude coordinate pair in WGS84 format\n * Format: [longitude, latitude]\n */\nexport type Coordinates = [longitude: number, latitude: number]\n\n/**\n * GeoJSON Point geometry\n */\nexport interface PointGeometry {\n type: 'Point'\n coordinates: Coordinates\n}\n\n/**\n * GeoJSON LineString geometry\n */\nexport interface LineStringGeometry {\n type: 'LineString'\n coordinates: Coordinates[]\n}\n\n/**\n * GeoJSON Polygon geometry\n */\nexport interface PolygonGeometry {\n type: 'Polygon'\n coordinates: Coordinates[][]\n}\n\n/**\n * Supported geometry types\n */\nexport type Geometry = PointGeometry | LineStringGeometry | PolygonGeometry\n\n/**\n * Feature metadata\n */\nexport interface FeatureProperties {\n /**\n * Human-readable description of the feature\n */\n description: string\n /**\n * The OS grid reference of the first coordinate of the feature\n */\n coordinateGridReference?: string\n /**\n * The OS grid reference of the centroid of the feature\n */\n centroidGridReference?: string\n}\n\n/**\n * A single GeoJSON Feature\n */\nexport interface Feature {\n id: string\n type: 'Feature'\n properties: FeatureProperties\n geometry: Geometry\n}\n\n/**\n * A GeoJSON FeatureCollection\n */\nexport type FeatureCollection = Feature[]\n\nexport type GeospatialState = FeatureCollection\n\nexport interface CheckAnswers {\n title?: ComponentText\n summaryList: SummaryList\n}\n\nexport interface SummaryList {\n classes?: string\n rows: SummaryListRow[]\n}\n\nexport interface SummaryListRow {\n key: ComponentText\n value: ComponentText\n actions?: { items: SummaryListAction[] }\n}\n\nexport type SummaryListAction = ComponentText & {\n href: string\n visuallyHiddenText: string\n}\n\nexport interface PageViewModelBase extends Partial<ViewContext> {\n page: PageController\n name?: string\n pageTitle: string\n sectionTitle?: string\n showTitle: boolean\n isStartPage: boolean\n backLink?: BackLink\n feedbackLink?: string\n serviceUrl: string\n phaseTag?: string\n}\n\nexport interface ItemDeletePageViewModel extends PageViewModelBase {\n context: FormContext\n itemTitle: string\n confirmation?: ComponentText\n buttonConfirm: ComponentText\n buttonCancel: ComponentText\n}\n\nexport interface FormPageViewModel extends PageViewModelBase {\n components: ComponentViewModel[]\n context: FormContext\n errors?: FormSubmissionError[]\n hasMissingNotificationEmail?: boolean\n allowSaveAndExit: boolean\n showSubmitButton?: boolean\n showPaymentExpiredNotification?: boolean\n}\n\nexport interface RepeaterSummaryPageViewModel extends PageViewModelBase {\n context: FormContext\n errors?: FormSubmissionError[]\n checkAnswers: CheckAnswers[]\n repeatTitle: string\n allowSaveAndExit: boolean\n}\n\nexport interface FeaturedFormPageViewModel extends FormPageViewModel {\n formAction?: string\n formComponent: ComponentViewModel\n componentsBefore: ComponentViewModel[]\n uploadId: string | undefined\n proxyUrl: string | null\n}\n\nexport type PageViewModel =\n | PageViewModelBase\n | ItemDeletePageViewModel\n | FormPageViewModel\n | RepeaterSummaryPageViewModel\n | FeaturedFormPageViewModel\n\nexport type GlobalFunction = (value: unknown) => unknown\nexport type FilterFunction = (value: unknown) => unknown\nexport interface ErrorMessageTemplate {\n type: string\n template: JoiExpression\n}\n\nexport interface ErrorMessageTemplateList {\n baseErrors: ErrorMessageTemplate[]\n advancedSettingsErrors: ErrorMessageTemplate[]\n}\n\nexport type PreparePageEventRequestOptions = (\n options: RequestOptions,\n event: Event,\n page: PageControllerClass,\n context: FormContext\n) => void\n\nexport type OnRequestCallback = (\n request: AnyFormRequest,\n h: FormResponseToolkit,\n context: FormContext\n) =>\n | ResponseObject\n | FormResponseToolkit['continue']\n | Promise<ResponseObject | FormResponseToolkit['continue']>\n\nexport type SaveAndExitHandler = (\n request: FormRequestPayload,\n h: FormResponseToolkit,\n context: FormContext\n) => ResponseObject\n\nexport interface ExternalArgs {\n component: ComponentDef\n controller: QuestionPageController\n sourceUrl: string\n actionArgs?: Record<string, string>\n isLive: boolean\n isPreview: boolean\n}\n\nexport interface PostcodeLookupExternalArgs extends ExternalArgs {\n component: UkAddressFieldComponent\n actionArgs: { step: string }\n}\n\nexport interface PaymentExternalArgs extends ExternalArgs {\n component: PaymentFieldComponent\n}\n\nexport interface ExternalStateAppendage {\n component: string\n data: FormStateValue | FormState\n}\n\nexport interface PluginOptions {\n model?: FormModel\n services?: Services\n controllers?: Record<string, typeof PageController>\n cache?: CacheService | string\n globals?: Record<string, GlobalFunction>\n filters?: Record<string, FilterFunction>\n saveAndExit?: SaveAndExitHandler\n pluginPath?: string\n nunjucks: {\n baseLayoutPath: string\n paths: string[]\n }\n viewContext: PluginProperties['forms-engine-plugin']['viewContext']\n preparePageEventRequestOptions?: PreparePageEventRequestOptions\n onRequest?: OnRequestCallback\n baseUrl: string // base URL of the application, protocol and hostname e.g. \"https://myapp.com\"\n ordnanceSurveyApiKey?: string\n ordnanceSurveyApiSecret?: string\n}\n\nexport interface FormAdapterSubmissionMessageMeta {\n schemaVersion: FormAdapterSubmissionSchemaVersion\n timestamp: Date\n referenceNumber: string\n formName: string\n formId: string\n formSlug: string\n status: FormStatus\n isPreview: boolean\n notificationEmail: string\n versionMetadata?: FormVersionMetadata\n custom?: Record<string, unknown>\n}\n\nexport type FormAdapterSubmissionMessageMetaSerialised = Omit<\n FormAdapterSubmissionMessageMeta,\n 'schemaVersion' | 'timestamp' | 'status'\n> & {\n schemaVersion: number\n status: string\n timestamp: string\n}\nexport interface FormAdapterFile {\n fileName: string\n fileId: string\n userDownloadLink: string\n}\n\nexport interface FormAdapterPayment {\n paymentId: string\n reference: string\n amount: number\n description: string\n createdAt: string\n}\n\nexport interface FormAdapterSubmissionMessageResult {\n files: {\n main: string\n repeaters: Record<string, string>\n }\n}\n\n/**\n * A detail item specifically for files\n */\nexport type FileUploadFieldDetailitem = Omit<DetailItemField, 'field'> & {\n field: FileUploadField\n}\n\n/**\n * A detail item specifically for payments\n */\nexport type PaymentFieldDetailItem = Omit<DetailItemField, 'field'> & {\n field: PaymentField\n}\nexport type RichFormValue =\n | FormValue\n | FormPayload\n | DatePartsState\n | MonthYearState\n | UkAddressState\n | EastingNorthingState\n | LatLongState\n | GeospatialState\n\nexport interface FormAdapterSubmissionMessageData {\n main: Record<string, RichFormValue | null>\n repeaters: Record<string, Record<string, RichFormValue>[]>\n files: Record<string, FormAdapterFile[]>\n payment?: FormAdapterPayment\n}\n\nexport interface FormAdapterSubmissionMessagePayload {\n meta: FormAdapterSubmissionMessageMeta\n data: FormAdapterSubmissionMessageData\n result: FormAdapterSubmissionMessageResult\n}\n\nexport interface FormAdapterSubmissionMessage extends FormAdapterSubmissionMessagePayload {\n messageId: string\n recordCreatedAt: Date\n}\n\nexport interface FormAdapterSubmissionService {\n handleFormSubmission: (\n submissionMessage: FormAdapterSubmissionMessage\n ) => unknown\n}\n"],"mappings":"AA0DA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AA2BA;AACA;AACA;AACA;;AAuGA,SACEA,UAAU,EACVC,YAAY;;AAgEd;AACA;AACA;AACA;;AAGA;AACA;AACA;;AAMA;AACA;AACA;;AAMA;AACA;AACA;;AAMA;AACA;AACA;;AAGA;AACA;AACA;;AAgBA;AACA;AACA;;AAQA;AACA;AACA;;AAyMA;AACA;AACA;;AAKA;AACA;AACA","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"types.js","names":["FileStatus","UploadStatus"],"sources":["../../../../src/server/plugins/engine/types.ts"],"sourcesContent":["import {\n type ComponentDef,\n type Event,\n type FormVersionMetadata,\n type Item,\n type List,\n type Page,\n type PaymentFieldComponent,\n type UkAddressFieldComponent\n} from '@defra/forms-model'\nimport {\n type PluginProperties,\n type Request,\n type ResponseObject\n} from '@hapi/hapi'\nimport { type JoiExpression, type ValidationErrorItem } from 'joi'\n\nimport { FormComponent } from '~/src/server/plugins/engine/components/FormComponent.js'\nimport { type UkAddressState } from '~/src/server/plugins/engine/components/UkAddressField.js'\nimport { type Component } from '~/src/server/plugins/engine/components/helpers/components.js'\nimport {\n type FileUploadField,\n type PaymentField\n} from '~/src/server/plugins/engine/components/index.js'\nimport {\n type BackLink,\n type ComponentText,\n type ComponentViewModel,\n type DatePartsState,\n type EastingNorthingState,\n type LatLongState,\n type MonthYearState\n} from '~/src/server/plugins/engine/components/types.js'\nimport { type FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport { type DetailItemField } from '~/src/server/plugins/engine/models/types.js'\nimport { type PageController } from '~/src/server/plugins/engine/pageControllers/PageController.js'\nimport { type PageControllerClass } from '~/src/server/plugins/engine/pageControllers/helpers/pages.js'\nimport { type QuestionPageController } from '~/src/server/plugins/engine/pageControllers/index.js'\nimport {\n type FileStatus,\n type FormAdapterSubmissionSchemaVersion,\n type UploadStatus\n} from '~/src/server/plugins/engine/types/enums.js'\nimport { type ViewContext } from '~/src/server/plugins/nunjucks/types.js'\nimport {\n type FormAction,\n type FormRequest,\n type FormRequestPayload,\n type FormResponseToolkit,\n type FormStatus\n} from '~/src/server/routes/types.js'\nimport { type CacheService } from '~/src/server/services/cacheService.js'\nimport { type RequestOptions } from '~/src/server/services/httpService.js'\nimport { type Services } from '~/src/server/types.js'\n\nexport type AnyFormRequest = FormRequest | FormRequestPayload\nexport type AnyRequest = Request | AnyFormRequest\n\n/**\n * Form submission state stores the following in Redis:\n * Props containing user's submitted values as `{ [inputId]: value }` or as `{ [sectionName]: { [inputName]: value } }`\n * a) . e.g:\n * ```ts\n * {\n * _C9PRHmsgt: 'Ben',\n * WfLk9McjzX: 'Music',\n * IK7jkUFCBL: 'Royal Academy of Music'\n * }\n * ```\n *\n * b)\n * ```ts\n * {\n * checkBeforeYouStart: { ukPassport: true },\n * applicantDetails: {\n * numberOfApplicants: 1,\n * phoneNumber: '77777777',\n * emailAddress: 'aaa@aaa.com'\n * },\n * applicantOneDetails: {\n * firstName: 'a',\n * middleName: 'a',\n * lastName: 'a',\n * address: { addressLine1: 'a', addressLine2: 'a', town: 'a', postcode: 'a' }\n * }\n * }\n * ```\n */\n\n/**\n * Form submission state\n */\nexport type FormSubmissionState = {\n upload?: Record<string, TempFileState>\n} & FormState\n\nexport interface FormSubmissionError extends Pick<\n ValidationErrorItem,\n 'context' | 'path'\n> {\n href: string // e.g: '#dateField__day'\n name: string // e.g: 'dateField__day'\n text: string // e.g: 'Date field must be a real date'\n}\n\nexport interface FormConfirmationState {\n confirmed?: true\n formId?: string\n referenceNumber?: string\n}\n\nexport interface FormPayloadParams {\n action?: FormAction\n confirm?: true\n crumb?: string\n itemId?: string\n}\n\n/**\n * Form POST for question pages\n * (after Joi has converted value types)\n */\nexport type FormPayload = FormPayloadParams & Partial<Record<string, FormValue>>\n\nexport type FormValue =\n | Item['value']\n | Item['value'][]\n | UploadState\n | RepeatListState\n | GeospatialState\n | undefined\n\nexport type FormState = Partial<Record<string, FormStateValue>>\nexport type FormStateValue = Exclude<FormValue, undefined> | null\n\nexport interface FormValidationResult<\n ValueType extends FormPayload | FormSubmissionState\n> {\n value: ValueType\n errors: FormSubmissionError[] | undefined\n}\n\nexport interface FormContext {\n /**\n * Evaluation form state only (filtered by visited paths),\n * with values formatted for condition evaluation using\n * {@link FormComponent.getContextValueFromState}\n */\n evaluationState: FormState\n\n /**\n * Relevant form state only (filtered by visited paths)\n */\n relevantState: FormState\n\n /**\n * Relevant pages only (filtered by visited paths)\n */\n relevantPages: PageControllerClass[]\n\n /**\n * Form submission payload (single page)\n */\n payload: FormPayload\n\n /**\n * Form submission state (entire form)\n */\n state: FormSubmissionState\n\n /**\n * Validation errors (entire form)\n */\n errors?: FormSubmissionError[]\n\n /**\n * Visited paths evaluated from form state\n */\n paths: string[]\n\n /**\n * Preview URL direct access is allowed\n */\n isForceAccess: boolean\n\n /**\n * Miscellaneous extra data from event responses\n */\n data: object\n\n pageDefMap: Map<string, Page>\n listDefMap: Map<string, List>\n componentDefMap: Map<string, ComponentDef>\n pageMap: Map<string, PageControllerClass>\n componentMap: Map<string, Component>\n referenceNumber: string\n}\n\nexport type FormContextRequest = (\n | {\n method: 'get'\n payload?: undefined\n }\n | {\n method: 'post'\n payload: FormPayload\n }\n | {\n method: FormRequest['method']\n payload?: object | undefined\n }\n) &\n Pick<\n FormRequest,\n 'app' | 'method' | 'params' | 'path' | 'query' | 'url' | 'server'\n >\n\nexport interface UploadInitiateResponse {\n uploadId: string\n uploadUrl: string\n statusUrl: string\n}\n\nexport {\n FileStatus,\n UploadStatus\n} from '~/src/server/plugins/engine/types/enums.js'\n\nexport type UploadState = FileState[]\n\nexport type FileUpload = {\n fileId: string\n filename: string\n contentLength: number\n} & (\n | {\n fileStatus: FileStatus.complete | FileStatus.rejected | FileStatus.pending\n errorMessage?: string\n }\n | {\n fileStatus: FileStatus.complete\n errorMessage?: undefined\n }\n)\n\nexport interface FileUploadMetadata {\n retrievalKey: string\n}\n\nexport type UploadStatusResponse =\n | {\n uploadStatus: UploadStatus.initiated\n metadata: FileUploadMetadata\n form: { file?: undefined }\n }\n | {\n uploadStatus: UploadStatus.pending | UploadStatus.ready\n metadata: FileUploadMetadata\n form: { file: FileUpload }\n numberOfRejectedFiles?: number\n }\n | {\n uploadStatus: UploadStatus.ready\n metadata: FileUploadMetadata\n form: { file: FileUpload }\n numberOfRejectedFiles: 0\n }\n\nexport type UploadStatusFileResponse = Exclude<\n UploadStatusResponse,\n { uploadStatus: UploadStatus.initiated }\n>\n\nexport interface FileState {\n uploadId: string\n status: UploadStatusFileResponse\n}\n\nexport interface TempFileState {\n upload?: UploadInitiateResponse\n files: UploadState\n}\n\nexport interface RepeatItemState extends FormPayload {\n itemId: string\n}\n\nexport type RepeatListState = RepeatItemState[]\n\n/**\n * A longitude/latitude coordinate pair in WGS84 format\n * Format: [longitude, latitude]\n */\nexport type Coordinates = [longitude: number, latitude: number]\n\n/**\n * GeoJSON Point geometry\n */\nexport interface PointGeometry {\n type: 'Point'\n coordinates: Coordinates\n}\n\n/**\n * GeoJSON LineString geometry\n */\nexport interface LineStringGeometry {\n type: 'LineString'\n coordinates: Coordinates[]\n}\n\n/**\n * GeoJSON Polygon geometry\n */\nexport interface PolygonGeometry {\n type: 'Polygon'\n coordinates: Coordinates[][]\n}\n\n/**\n * Supported geometry types\n */\nexport type Geometry = PointGeometry | LineStringGeometry | PolygonGeometry\n\n/**\n * Feature metadata\n */\nexport interface FeatureProperties {\n /**\n * Human-readable description of the feature\n */\n description: string\n /**\n * The OS grid reference of the first coordinate of the feature\n */\n coordinateGridReference?: string\n /**\n * The OS grid reference of the centroid of the feature\n */\n centroidGridReference?: string\n}\n\n/**\n * A single GeoJSON Feature\n */\nexport interface Feature {\n id: string\n type: 'Feature'\n properties: FeatureProperties\n geometry: Geometry\n}\n\n/**\n * A GeoJSON FeatureCollection\n */\nexport type FeatureCollection = Feature[]\n\nexport type GeospatialState = FeatureCollection\n\nexport interface CheckAnswers {\n title?: ComponentText\n summaryList: SummaryList\n}\n\nexport interface SummaryList {\n classes?: string\n rows: SummaryListRow[]\n}\n\nexport interface SummaryListRow {\n key: ComponentText\n value: ComponentText\n actions?: { items: SummaryListAction[] }\n}\n\nexport type SummaryListAction = ComponentText & {\n href: string\n visuallyHiddenText: string\n}\n\nexport interface PageViewModelBase extends Partial<ViewContext> {\n page: PageController\n name?: string\n pageTitle: string\n sectionTitle?: string\n showTitle: boolean\n isStartPage: boolean\n backLink?: BackLink\n feedbackLink?: string\n serviceUrl: string\n phaseTag?: string\n}\n\nexport interface ItemDeletePageViewModel extends PageViewModelBase {\n context: FormContext\n itemTitle: string\n confirmation?: ComponentText\n buttonConfirm: ComponentText\n buttonCancel: ComponentText\n}\n\nexport interface FormPageViewModel extends PageViewModelBase {\n components: ComponentViewModel[]\n context: FormContext\n errors?: FormSubmissionError[]\n hasMissingNotificationEmail?: boolean\n allowSaveAndExit: boolean\n showSubmitButton?: boolean\n showPaymentExpiredNotification?: boolean\n}\n\nexport interface RepeaterSummaryPageViewModel extends PageViewModelBase {\n context: FormContext\n errors?: FormSubmissionError[]\n checkAnswers: CheckAnswers[]\n repeatTitle: string\n allowSaveAndExit: boolean\n}\n\nexport interface FeaturedFormPageViewModel extends FormPageViewModel {\n formAction?: string\n formComponent: ComponentViewModel\n componentsBefore: ComponentViewModel[]\n uploadId: string | undefined\n proxyUrl: string | null\n}\n\nexport type PageViewModel =\n | PageViewModelBase\n | ItemDeletePageViewModel\n | FormPageViewModel\n | RepeaterSummaryPageViewModel\n | FeaturedFormPageViewModel\n\nexport type GlobalFunction = (value: unknown) => unknown\nexport type FilterFunction = (value: unknown) => unknown\nexport interface ErrorMessageTemplate {\n type: string\n template: JoiExpression\n}\n\nexport interface ErrorMessageTemplateList {\n baseErrors: ErrorMessageTemplate[]\n advancedSettingsErrors: ErrorMessageTemplate[]\n}\n\nexport type PreparePageEventRequestOptions = (\n options: RequestOptions,\n event: Event,\n page: PageControllerClass,\n context: FormContext\n) => void\n\nexport type OnRequestCallback = (\n request: AnyFormRequest,\n h: FormResponseToolkit,\n context: FormContext\n) =>\n | ResponseObject\n | FormResponseToolkit['continue']\n | Promise<ResponseObject | FormResponseToolkit['continue']>\n\nexport type SaveAndExitHandler = (\n request: FormRequestPayload,\n h: FormResponseToolkit,\n context: FormContext\n) => ResponseObject\n\nexport interface ExternalArgs {\n component: ComponentDef\n controller: QuestionPageController\n sourceUrl: string\n actionArgs?: Record<string, string>\n isLive: boolean\n isPreview: boolean\n}\n\nexport interface PostcodeLookupExternalArgs extends ExternalArgs {\n component: UkAddressFieldComponent\n actionArgs: { step: string }\n}\n\nexport interface PaymentExternalArgs extends ExternalArgs {\n component: PaymentFieldComponent\n}\n\nexport interface ExternalStateAppendage {\n component: string\n data: FormStateValue | FormState\n}\n\nexport interface PluginOptions {\n model?: FormModel\n services?: Services\n controllers?: Record<string, typeof PageController>\n cache?: CacheService | string\n globals?: Record<string, GlobalFunction>\n filters?: Record<string, FilterFunction>\n saveAndExit?: SaveAndExitHandler\n pluginPath?: string\n nunjucks: {\n baseLayoutPath: string\n paths: string[]\n }\n viewContext: PluginProperties['forms-engine-plugin']['viewContext']\n preparePageEventRequestOptions?: PreparePageEventRequestOptions\n onRequest?: OnRequestCallback\n baseUrl: string // base URL of the application, protocol and hostname e.g. \"https://myapp.com\"\n ordnanceSurveyApiKey?: string\n ordnanceSurveyApiSecret?: string\n}\n\nexport interface FormAdapterSubmissionMessageMeta {\n schemaVersion: FormAdapterSubmissionSchemaVersion\n timestamp: Date\n referenceNumber: string\n formName: string\n formId: string\n formSlug: string\n status: FormStatus\n isPreview: boolean\n notificationEmail: string\n versionMetadata?: FormVersionMetadata\n custom?: Record<string, unknown>\n}\n\nexport type FormAdapterSubmissionMessageMetaSerialised = Omit<\n FormAdapterSubmissionMessageMeta,\n 'schemaVersion' | 'timestamp' | 'status'\n> & {\n schemaVersion: number\n status: string\n timestamp: string\n}\nexport interface FormAdapterFile {\n fileName: string\n fileId: string\n userDownloadLink: string\n}\n\nexport interface FormAdapterPayment {\n paymentId: string\n reference: string\n amount: number\n description: string\n createdAt: string\n}\n\nexport interface FormAdapterSubmissionMessageResult {\n files: {\n main: string\n repeaters: Record<string, string>\n }\n}\n\n/**\n * A detail item specifically for files\n */\nexport type FileUploadFieldDetailitem = Omit<DetailItemField, 'field'> & {\n field: FileUploadField\n}\n\n/**\n * A detail item specifically for payments\n */\nexport type PaymentFieldDetailItem = Omit<DetailItemField, 'field'> & {\n field: PaymentField\n}\nexport type RichFormValue =\n | FormValue\n | FormPayload\n | DatePartsState\n | MonthYearState\n | UkAddressState\n | EastingNorthingState\n | LatLongState\n | GeospatialState\n\nexport interface FormAdapterSubmissionMessageData {\n main: Record<string, RichFormValue | null>\n repeaters: Record<string, Record<string, RichFormValue>[]>\n files: Record<string, FormAdapterFile[]>\n payment?: FormAdapterPayment\n}\n\nexport interface FormAdapterSubmissionMessagePayload {\n meta: FormAdapterSubmissionMessageMeta\n data: FormAdapterSubmissionMessageData\n result: FormAdapterSubmissionMessageResult\n}\n\nexport interface FormAdapterSubmissionMessage extends FormAdapterSubmissionMessagePayload {\n messageId: string\n recordCreatedAt: Date\n}\n\nexport interface FormAdapterSubmissionService {\n handleFormSubmission: (\n submissionMessage: FormAdapterSubmissionMessage\n ) => unknown\n}\n"],"mappings":"AA0DA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AA2BA;AACA;AACA;AACA;;AAsGA,SACEA,UAAU,EACVC,YAAY;;AAgEd;AACA;AACA;AACA;;AAGA;AACA;AACA;;AAMA;AACA;AACA;;AAMA;AACA;AACA;;AAMA;AACA;AACA;;AAGA;AACA;AACA;;AAgBA;AACA;AACA;;AAQA;AACA;AACA;;AAyMA;AACA;AACA;;AAKA;AACA;AACA","ignoreList":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@defra/forms-engine-plugin",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.7.0",
|
|
4
4
|
"description": "Defra forms engine",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
},
|
|
84
84
|
"license": "SEE LICENSE IN LICENSE",
|
|
85
85
|
"dependencies": {
|
|
86
|
-
"@defra/forms-model": "^3.0.
|
|
86
|
+
"@defra/forms-model": "^3.0.647",
|
|
87
87
|
"@defra/hapi-tracing": "^1.29.0",
|
|
88
88
|
"@defra/interactive-map": "^0.0.17-alpha",
|
|
89
89
|
"@elastic/ecs-pino-format": "^1.5.0",
|
|
@@ -183,7 +183,6 @@ describe('getFormModel helper', () => {
|
|
|
183
183
|
definition,
|
|
184
184
|
{
|
|
185
185
|
basePath: slug,
|
|
186
|
-
versionNumber: 17,
|
|
187
186
|
ordnanceSurveyApiKey: undefined,
|
|
188
187
|
formId: metadata.id
|
|
189
188
|
},
|
|
@@ -288,7 +287,6 @@ describe('resolveFormModel helper', () => {
|
|
|
288
287
|
definition,
|
|
289
288
|
expect.objectContaining({
|
|
290
289
|
basePath: 'forms/preview/live/tb-origin',
|
|
291
|
-
versionNumber: 9,
|
|
292
290
|
ordnanceSurveyApiKey: 'os-api-key',
|
|
293
291
|
formId: metadata.id
|
|
294
292
|
}),
|
|
@@ -5,8 +5,7 @@ import { isEqual } from 'date-fns'
|
|
|
5
5
|
import { PREVIEW_PATH_PREFIX } from '~/src/server/constants.js'
|
|
6
6
|
import {
|
|
7
7
|
checkEmailAddressForLiveFormSubmission,
|
|
8
|
-
getCacheService
|
|
9
|
-
getFormVersion
|
|
8
|
+
getCacheService
|
|
10
9
|
} from '~/src/server/plugins/engine/helpers.js'
|
|
11
10
|
import { FormModel } from '~/src/server/plugins/engine/models/index.js'
|
|
12
11
|
import { type PageController } from '~/src/server/plugins/engine/pageControllers/PageController.js'
|
|
@@ -65,15 +64,12 @@ export async function getFormModel(
|
|
|
65
64
|
)
|
|
66
65
|
}
|
|
67
66
|
|
|
68
|
-
const versionNumber = getFormVersion(definition)?.versionNumber
|
|
69
|
-
|
|
70
67
|
return new FormModel(
|
|
71
68
|
definition,
|
|
72
69
|
{
|
|
73
70
|
basePath:
|
|
74
71
|
options.basePath ??
|
|
75
72
|
buildBasePath(options.routePrefix ?? '', slug, formState, isPreview),
|
|
76
|
-
versionNumber,
|
|
77
73
|
ordnanceSurveyApiKey: options.ordnanceSurveyApiKey,
|
|
78
74
|
formId: options.formId ?? metadata.id
|
|
79
75
|
},
|
|
@@ -182,15 +178,12 @@ export async function resolveFormModel(
|
|
|
182
178
|
const routePrefix =
|
|
183
179
|
options.routePrefix ?? server.realm.modifiers.route.prefix
|
|
184
180
|
|
|
185
|
-
const versionNumber = getFormVersion(definition)?.versionNumber
|
|
186
|
-
|
|
187
181
|
const model = new FormModel(
|
|
188
182
|
definition,
|
|
189
183
|
{
|
|
190
184
|
basePath:
|
|
191
185
|
options.basePath ??
|
|
192
186
|
buildBasePath(routePrefix, slug, formState, isPreview),
|
|
193
|
-
versionNumber,
|
|
194
187
|
ordnanceSurveyApiKey: options.ordnanceSurveyApiKey,
|
|
195
188
|
formId: options.formId ?? metadata.id
|
|
196
189
|
},
|
|
@@ -173,6 +173,61 @@ describe.each([
|
|
|
173
173
|
)
|
|
174
174
|
})
|
|
175
175
|
|
|
176
|
+
it('is configured with min/max items', () => {
|
|
177
|
+
const collectionLimited = new ComponentCollection(
|
|
178
|
+
[{ ...def, schema: { min: 2, max: 4 } }],
|
|
179
|
+
{ model }
|
|
180
|
+
)
|
|
181
|
+
const { formSchema } = collectionLimited
|
|
182
|
+
const { keys } = formSchema.describe()
|
|
183
|
+
|
|
184
|
+
expect(keys).toHaveProperty(
|
|
185
|
+
'myComponent',
|
|
186
|
+
expect.objectContaining({
|
|
187
|
+
items: [
|
|
188
|
+
{
|
|
189
|
+
allow: options.allow,
|
|
190
|
+
flags: {
|
|
191
|
+
label: def.shortDescription,
|
|
192
|
+
only: true
|
|
193
|
+
},
|
|
194
|
+
type: options.list.type
|
|
195
|
+
}
|
|
196
|
+
],
|
|
197
|
+
rules: [
|
|
198
|
+
{ args: { limit: 2 }, name: 'min' },
|
|
199
|
+
{ args: { limit: 4 }, name: 'max' }
|
|
200
|
+
]
|
|
201
|
+
})
|
|
202
|
+
)
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
it('is configured with length items', () => {
|
|
206
|
+
const collectionLimited = new ComponentCollection(
|
|
207
|
+
[{ ...def, schema: { length: 3 } }],
|
|
208
|
+
{ model }
|
|
209
|
+
)
|
|
210
|
+
const { formSchema } = collectionLimited
|
|
211
|
+
const { keys } = formSchema.describe()
|
|
212
|
+
|
|
213
|
+
expect(keys).toHaveProperty(
|
|
214
|
+
'myComponent',
|
|
215
|
+
expect.objectContaining({
|
|
216
|
+
items: [
|
|
217
|
+
{
|
|
218
|
+
allow: options.allow,
|
|
219
|
+
flags: {
|
|
220
|
+
label: def.shortDescription,
|
|
221
|
+
only: true
|
|
222
|
+
},
|
|
223
|
+
type: options.list.type
|
|
224
|
+
}
|
|
225
|
+
],
|
|
226
|
+
rules: [{ args: { limit: 3 }, name: 'length' }]
|
|
227
|
+
})
|
|
228
|
+
)
|
|
229
|
+
})
|
|
230
|
+
|
|
176
231
|
it('adds errors for empty value', () => {
|
|
177
232
|
const result = collection.validate(getFormData())
|
|
178
233
|
|
|
@@ -386,7 +441,7 @@ describe.each([
|
|
|
386
441
|
it('should return errors', () => {
|
|
387
442
|
const errors = field.getAllPossibleErrors()
|
|
388
443
|
expect(errors.baseErrors).not.toBeEmpty()
|
|
389
|
-
expect(errors.advancedSettingsErrors).toBeEmpty()
|
|
444
|
+
expect(errors.advancedSettingsErrors).not.toBeEmpty()
|
|
390
445
|
})
|
|
391
446
|
})
|
|
392
447
|
|
|
@@ -5,7 +5,9 @@ import { isFormValue } from '~/src/server/plugins/engine/components/FormComponen
|
|
|
5
5
|
import { SelectionControlField } from '~/src/server/plugins/engine/components/SelectionControlField.js'
|
|
6
6
|
import { type FormModel } from '~/src/server/plugins/engine/models/FormModel.js'
|
|
7
7
|
import { type QuestionPageController } from '~/src/server/plugins/engine/pageControllers/QuestionPageController.js'
|
|
8
|
+
import { messageTemplate } from '~/src/server/plugins/engine/pageControllers/validationOptions.js'
|
|
8
9
|
import {
|
|
10
|
+
type ErrorMessageTemplateList,
|
|
9
11
|
type FormState,
|
|
10
12
|
type FormStateValue,
|
|
11
13
|
type FormSubmissionState
|
|
@@ -13,6 +15,7 @@ import {
|
|
|
13
15
|
|
|
14
16
|
export class CheckboxesField extends SelectionControlField {
|
|
15
17
|
declare options: CheckboxesFieldComponent['options']
|
|
18
|
+
declare schema: CheckboxesFieldComponent['schema']
|
|
16
19
|
declare formSchema: ArraySchema<string> | ArraySchema<number>
|
|
17
20
|
declare stateSchema: ArraySchema<string> | ArraySchema<number>
|
|
18
21
|
|
|
@@ -24,6 +27,7 @@ export class CheckboxesField extends SelectionControlField {
|
|
|
24
27
|
|
|
25
28
|
const { listType: type } = this
|
|
26
29
|
const { options } = def
|
|
30
|
+
const schema = 'schema' in def ? def.schema : {}
|
|
27
31
|
|
|
28
32
|
let formSchema =
|
|
29
33
|
type === 'string' ? joi.array<string>() : joi.array<number>()
|
|
@@ -42,6 +46,18 @@ export class CheckboxesField extends SelectionControlField {
|
|
|
42
46
|
formSchema = formSchema.optional()
|
|
43
47
|
}
|
|
44
48
|
|
|
49
|
+
if (typeof schema?.length === 'number') {
|
|
50
|
+
formSchema = formSchema.length(schema.length)
|
|
51
|
+
} else {
|
|
52
|
+
if (typeof schema?.min === 'number') {
|
|
53
|
+
formSchema = formSchema.min(schema.min)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (typeof schema?.max === 'number') {
|
|
57
|
+
formSchema = formSchema.max(schema.max)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
45
61
|
this.formSchema = formSchema.default([])
|
|
46
62
|
this.stateSchema = formSchema.default(null).allow(null)
|
|
47
63
|
this.options = options
|
|
@@ -112,6 +128,30 @@ export class CheckboxesField extends SelectionControlField {
|
|
|
112
128
|
return this.getContextValueFromFormValue(values)
|
|
113
129
|
}
|
|
114
130
|
|
|
131
|
+
/**
|
|
132
|
+
* For error preview page that shows all possible errors on a component
|
|
133
|
+
*/
|
|
134
|
+
getAllPossibleErrors(): ErrorMessageTemplateList {
|
|
135
|
+
return CheckboxesField.getAllPossibleErrors()
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Static version of getAllPossibleErrors that doesn't require a component instance.
|
|
140
|
+
*/
|
|
141
|
+
static getAllPossibleErrors(): ErrorMessageTemplateList {
|
|
142
|
+
const parentErrors = SelectionControlField.getAllPossibleErrors()
|
|
143
|
+
|
|
144
|
+
return {
|
|
145
|
+
...parentErrors,
|
|
146
|
+
advancedSettingsErrors: [
|
|
147
|
+
...parentErrors.advancedSettingsErrors,
|
|
148
|
+
{ type: 'array.min', template: messageTemplate.arrayMin },
|
|
149
|
+
{ type: 'array.max', template: messageTemplate.arrayMax },
|
|
150
|
+
{ type: 'array.length', template: messageTemplate.arrayLength }
|
|
151
|
+
]
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
115
155
|
isValue(value?: FormStateValue | FormState): value is Item['value'][] {
|
|
116
156
|
if (!Array.isArray(value)) {
|
|
117
157
|
return false
|
|
@@ -141,21 +141,6 @@ describe('FormModel', () => {
|
|
|
141
141
|
expect(model.schemaVersion).toBe(SchemaVersion.V1)
|
|
142
142
|
})
|
|
143
143
|
|
|
144
|
-
it('sets versionNumber from options', () => {
|
|
145
|
-
const model = new FormModel(definition, {
|
|
146
|
-
basePath: 'test',
|
|
147
|
-
versionNumber: 42
|
|
148
|
-
})
|
|
149
|
-
|
|
150
|
-
expect(model.versionNumber).toBe(42)
|
|
151
|
-
})
|
|
152
|
-
|
|
153
|
-
it('sets versionNumber to undefined when not provided', () => {
|
|
154
|
-
const model = new FormModel(definition, { basePath: 'test' })
|
|
155
|
-
|
|
156
|
-
expect(model.versionNumber).toBeUndefined()
|
|
157
|
-
})
|
|
158
|
-
|
|
159
144
|
it.each([
|
|
160
145
|
{
|
|
161
146
|
input: undefined,
|
|
@@ -344,55 +329,6 @@ describe('FormModel', () => {
|
|
|
344
329
|
)
|
|
345
330
|
})
|
|
346
331
|
|
|
347
|
-
it('includes submittedVersionNumber in context when versionNumber is set', () => {
|
|
348
|
-
const formModel = new FormModel(fieldsRequiredDefinition, {
|
|
349
|
-
basePath: '/components',
|
|
350
|
-
versionNumber: 123
|
|
351
|
-
})
|
|
352
|
-
|
|
353
|
-
const state = {
|
|
354
|
-
$$__referenceNumber: 'foobar'
|
|
355
|
-
}
|
|
356
|
-
const pageUrl = new URL('http://example.com/components/fields-required')
|
|
357
|
-
|
|
358
|
-
const request: FormContextRequest = buildFormContextRequest({
|
|
359
|
-
method: 'get',
|
|
360
|
-
query: {},
|
|
361
|
-
path: pageUrl.pathname,
|
|
362
|
-
params: { path: 'components', slug: 'fields-required' },
|
|
363
|
-
url: pageUrl,
|
|
364
|
-
app: { model: formModel }
|
|
365
|
-
})
|
|
366
|
-
|
|
367
|
-
const context = formModel.getFormContext(request, state)
|
|
368
|
-
|
|
369
|
-
expect(context.submittedVersionNumber).toBe(123)
|
|
370
|
-
})
|
|
371
|
-
|
|
372
|
-
it('sets submittedVersionNumber to undefined when versionNumber is not set', () => {
|
|
373
|
-
const formModel = new FormModel(fieldsRequiredDefinition, {
|
|
374
|
-
basePath: '/components'
|
|
375
|
-
})
|
|
376
|
-
|
|
377
|
-
const state = {
|
|
378
|
-
$$__referenceNumber: 'foobar'
|
|
379
|
-
}
|
|
380
|
-
const pageUrl = new URL('http://example.com/components/fields-required')
|
|
381
|
-
|
|
382
|
-
const request: FormContextRequest = buildFormContextRequest({
|
|
383
|
-
method: 'get',
|
|
384
|
-
query: {},
|
|
385
|
-
path: pageUrl.pathname,
|
|
386
|
-
params: { path: 'components', slug: 'fields-required' },
|
|
387
|
-
url: pageUrl,
|
|
388
|
-
app: { model: formModel }
|
|
389
|
-
})
|
|
390
|
-
|
|
391
|
-
const context = formModel.getFormContext(request, state)
|
|
392
|
-
|
|
393
|
-
expect(context.submittedVersionNumber).toBeUndefined()
|
|
394
|
-
})
|
|
395
|
-
|
|
396
332
|
it('redirects to the page if the list field (radio) is invalidated due to list item conditions', () => {
|
|
397
333
|
const formModel = new FormModel(conditionsListDefinition, {
|
|
398
334
|
basePath: '/conditional-list-items'
|
|
@@ -78,7 +78,6 @@ export class FormModel {
|
|
|
78
78
|
formId: string
|
|
79
79
|
values: FormDefinition
|
|
80
80
|
basePath: string
|
|
81
|
-
versionNumber?: number
|
|
82
81
|
ordnanceSurveyApiKey?: string
|
|
83
82
|
conditions: Partial<Record<string, ExecutableCondition>>
|
|
84
83
|
pages: PageControllerClass[]
|
|
@@ -100,7 +99,6 @@ export class FormModel {
|
|
|
100
99
|
def: typeof this.def,
|
|
101
100
|
options: {
|
|
102
101
|
basePath: string
|
|
103
|
-
versionNumber?: number
|
|
104
102
|
ordnanceSurveyApiKey?: string
|
|
105
103
|
formId?: string
|
|
106
104
|
},
|
|
@@ -158,7 +156,6 @@ export class FormModel {
|
|
|
158
156
|
this.formId = options.formId ?? ''
|
|
159
157
|
this.values = result.value
|
|
160
158
|
this.basePath = options.basePath
|
|
161
|
-
this.versionNumber = options.versionNumber
|
|
162
159
|
this.ordnanceSurveyApiKey = options.ordnanceSurveyApiKey
|
|
163
160
|
this.conditions = {}
|
|
164
161
|
this.services = services
|
|
@@ -362,8 +359,7 @@ export class FormModel {
|
|
|
362
359
|
componentDefMap: this.componentDefMap,
|
|
363
360
|
pageMap: this.pageMap,
|
|
364
361
|
componentMap: this.componentMap,
|
|
365
|
-
referenceNumber: getReferenceNumber(state)
|
|
366
|
-
submittedVersionNumber: this.versionNumber
|
|
362
|
+
referenceNumber: getReferenceNumber(state)
|
|
367
363
|
}
|
|
368
364
|
|
|
369
365
|
// Validate current page
|