@defra/forms-engine-plugin 4.0.14 → 4.0.16
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/components/DeclarationField.d.ts +2 -2
- package/.server/server/plugins/engine/components/DeclarationField.js +6 -5
- package/.server/server/plugins/engine/components/DeclarationField.js.map +1 -1
- package/.server/server/plugins/engine/models/SummaryViewModel.d.ts +11 -2
- package/.server/server/plugins/engine/models/SummaryViewModel.js +3 -3
- package/.server/server/plugins/engine/models/SummaryViewModel.js.map +1 -1
- package/.server/server/plugins/engine/pageControllers/RepeatPageController.js +1 -1
- package/.server/server/plugins/engine/pageControllers/RepeatPageController.js.map +1 -1
- package/package.json +2 -2
- package/src/server/plugins/engine/components/DeclarationField.test.ts +68 -11
- package/src/server/plugins/engine/components/DeclarationField.ts +7 -5
- package/src/server/plugins/engine/components/helpers/components.test.ts +85 -0
- package/src/server/plugins/engine/models/SummaryViewModel.test.ts +51 -2
- package/src/server/plugins/engine/models/SummaryViewModel.ts +6 -3
- package/src/server/plugins/engine/pageControllers/RepeatPageController.ts +1 -1
|
@@ -10,7 +10,7 @@ export declare class DeclarationField extends FormComponent {
|
|
|
10
10
|
stateSchema: BooleanSchema;
|
|
11
11
|
content: string;
|
|
12
12
|
constructor(def: DeclarationFieldComponent, props: ConstructorParameters<typeof FormComponent>[1]);
|
|
13
|
-
getFormValueFromState(state: FormSubmissionState): "true" |
|
|
13
|
+
getFormValueFromState(state: FormSubmissionState): "true" | "unchecked";
|
|
14
14
|
getFormDataFromState(state: FormSubmissionState): FormPayload;
|
|
15
15
|
getStateFromValidForm(payload: FormPayload): FormState;
|
|
16
16
|
getContextValueFromFormValue(value: FormValue | FormPayload): boolean;
|
|
@@ -26,10 +26,10 @@ export declare class DeclarationField extends FormComponent {
|
|
|
26
26
|
};
|
|
27
27
|
};
|
|
28
28
|
content: string;
|
|
29
|
-
values: FormValue;
|
|
30
29
|
items: {
|
|
31
30
|
text: string;
|
|
32
31
|
value: string;
|
|
32
|
+
checked: boolean;
|
|
33
33
|
}[];
|
|
34
34
|
label: {
|
|
35
35
|
text: string;
|
|
@@ -28,14 +28,14 @@ export class DeclarationField extends FormComponent {
|
|
|
28
28
|
const {
|
|
29
29
|
name
|
|
30
30
|
} = this;
|
|
31
|
-
return state[name] === true ? 'true' :
|
|
31
|
+
return state[name] === true ? 'true' : 'unchecked';
|
|
32
32
|
}
|
|
33
33
|
getFormDataFromState(state) {
|
|
34
34
|
const {
|
|
35
35
|
name
|
|
36
36
|
} = this;
|
|
37
37
|
return {
|
|
38
|
-
[name]: state[name] === true ? 'true' :
|
|
38
|
+
[name]: state[name] === true ? 'true' : 'unchecked'
|
|
39
39
|
};
|
|
40
40
|
}
|
|
41
41
|
getStateFromValidForm(payload) {
|
|
@@ -57,7 +57,7 @@ export class DeclarationField extends FormComponent {
|
|
|
57
57
|
return this.isValue(value) ? value : undefined;
|
|
58
58
|
}
|
|
59
59
|
getDisplayStringFromFormValue(value) {
|
|
60
|
-
return value ? this.declarationConfirmationLabel : '';
|
|
60
|
+
return value === 'true' ? this.declarationConfirmationLabel : 'Not provided';
|
|
61
61
|
}
|
|
62
62
|
getViewModel(payload, errors) {
|
|
63
63
|
const defaultDeclarationConfirmationLabel = 'I confirm that I understand and accept this declaration';
|
|
@@ -67,6 +67,7 @@ export class DeclarationField extends FormComponent {
|
|
|
67
67
|
content,
|
|
68
68
|
declarationConfirmationLabel = defaultDeclarationConfirmationLabel
|
|
69
69
|
} = this;
|
|
70
|
+
const isChecked = payload[this.name] === 'true' || payload[this.name] === true;
|
|
70
71
|
return {
|
|
71
72
|
...super.getViewModel(payload, errors),
|
|
72
73
|
hint: hint ? {
|
|
@@ -78,10 +79,10 @@ export class DeclarationField extends FormComponent {
|
|
|
78
79
|
}
|
|
79
80
|
},
|
|
80
81
|
content,
|
|
81
|
-
values: payload[this.name],
|
|
82
82
|
items: [{
|
|
83
83
|
text: declarationConfirmationLabel,
|
|
84
|
-
value: 'true'
|
|
84
|
+
value: 'true',
|
|
85
|
+
checked: isChecked
|
|
85
86
|
}]
|
|
86
87
|
};
|
|
87
88
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DeclarationField.js","names":["joi","FormComponent","isFormValue","messageTemplate","DeclarationField","DEFAULT_DECLARATION_LABEL","constructor","def","props","options","content","checkboxSchema","string","valid","required","formSchema","array","items","strip","label","single","messages","declarationRequired","stateSchema","boolean","cast","declarationConfirmationLabel","getFormValueFromState","state","name","
|
|
1
|
+
{"version":3,"file":"DeclarationField.js","names":["joi","FormComponent","isFormValue","messageTemplate","DeclarationField","DEFAULT_DECLARATION_LABEL","constructor","def","props","options","content","checkboxSchema","string","valid","required","formSchema","array","items","strip","label","single","messages","declarationRequired","stateSchema","boolean","cast","declarationConfirmationLabel","getFormValueFromState","state","name","getFormDataFromState","getStateFromValidForm","payload","payloadValue","value","isValue","length","every","v","getContextValueFromFormValue","getFormValue","undefined","getDisplayStringFromFormValue","getViewModel","errors","defaultDeclarationConfirmationLabel","title","hint","isChecked","text","fieldset","legend","checked","Array","isArray","getAllPossibleErrors","baseErrors","type","template","advancedSettingsErrors","isBool"],"sources":["../../../../../src/server/plugins/engine/components/DeclarationField.ts"],"sourcesContent":["import { type DeclarationFieldComponent, type Item } from '@defra/forms-model'\nimport joi, {\n type ArraySchema,\n type BooleanSchema,\n type StringSchema\n} from 'joi'\n\nimport {\n FormComponent,\n isFormValue\n} 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 FormState,\n type FormStateValue,\n type FormSubmissionError,\n type FormSubmissionState,\n type FormValue\n} from '~/src/server/plugins/engine/types.js'\n\nexport class DeclarationField extends FormComponent {\n private readonly DEFAULT_DECLARATION_LABEL = 'I understand and agree'\n\n declare options: DeclarationFieldComponent['options']\n\n declare declarationConfirmationLabel: string\n\n declare formSchema: ArraySchema<StringSchema[]>\n declare stateSchema: BooleanSchema\n declare content: string\n\n constructor(\n def: DeclarationFieldComponent,\n props: ConstructorParameters<typeof FormComponent>[1]\n ) {\n super(def, props)\n\n const { options, content } = def\n\n let checkboxSchema = joi.string().valid('true')\n\n if (options.required !== false) {\n checkboxSchema = checkboxSchema.required()\n }\n\n const formSchema = joi\n .array()\n .items(checkboxSchema, joi.string().valid('unchecked').strip())\n .label(this.label)\n .single()\n .messages({\n 'any.required': messageTemplate.declarationRequired as string,\n 'any.unknown': messageTemplate.declarationRequired as string,\n 'array.includesRequiredUnknowns':\n messageTemplate.declarationRequired as string\n }) as ArraySchema<StringSchema[]>\n\n this.formSchema = formSchema\n this.stateSchema = joi.boolean().cast('string').label(this.label).required()\n\n this.options = options\n this.content = content\n this.declarationConfirmationLabel =\n options.declarationConfirmationLabel ?? this.DEFAULT_DECLARATION_LABEL\n }\n\n getFormValueFromState(state: FormSubmissionState) {\n const { name } = this\n return state[name] === true ? 'true' : 'unchecked'\n }\n\n getFormDataFromState(state: FormSubmissionState): FormPayload {\n const { name } = this\n return { [name]: state[name] === true ? 'true' : 'unchecked' }\n }\n\n getStateFromValidForm(payload: FormPayload): FormState {\n const { name } = this\n const payloadValue = payload[name]\n const value =\n this.isValue(payloadValue) &&\n payloadValue.length > 0 &&\n payloadValue.every((v) => {\n return v === 'true'\n })\n\n return { [name]: value }\n }\n\n getContextValueFromFormValue(value: FormValue | FormPayload): boolean {\n return value === 'true'\n }\n\n getFormValue(value?: FormStateValue | FormState) {\n return this.isValue(value) ? value : undefined\n }\n\n getDisplayStringFromFormValue(value: FormValue | FormPayload): string {\n return value === 'true' ? this.declarationConfirmationLabel : 'Not provided'\n }\n\n getViewModel(payload: FormPayload, errors?: FormSubmissionError[]) {\n const defaultDeclarationConfirmationLabel =\n 'I confirm that I understand and accept this declaration'\n const {\n title,\n hint,\n content,\n declarationConfirmationLabel = defaultDeclarationConfirmationLabel\n } = this\n const isChecked =\n payload[this.name] === 'true' || payload[this.name] === true\n return {\n ...super.getViewModel(payload, errors),\n hint: hint ? { text: hint } : undefined,\n fieldset: {\n legend: {\n text: title\n }\n },\n content,\n items: [\n {\n text: declarationConfirmationLabel,\n value: 'true',\n checked: isChecked\n }\n ]\n }\n }\n\n isValue(value?: FormStateValue | FormState): value is Item['value'][] {\n if (!Array.isArray(value)) {\n return false\n }\n\n // Skip checks when empty\n if (!value.length) {\n return true\n }\n\n return value.every(isFormValue)\n }\n\n /**\n * For error preview page that shows all possible errors on a component\n */\n getAllPossibleErrors(): ErrorMessageTemplateList {\n return DeclarationField.getAllPossibleErrors()\n }\n\n /**\n * Static version of getAllPossibleErrors that doesn't require a component instance.\n */\n static getAllPossibleErrors(): ErrorMessageTemplateList {\n return {\n baseErrors: [\n { type: 'required', template: messageTemplate.declarationRequired }\n ],\n advancedSettingsErrors: []\n }\n }\n\n static isBool(value?: FormStateValue | FormState): value is boolean {\n return isFormValue(value) && typeof value === 'boolean'\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { type Field } from '~/src/server/plugins/engine/components/helpers/components.js';
|
|
1
2
|
import { type BackLink, type ComponentViewModel } from '~/src/server/plugins/engine/components/types.js';
|
|
2
|
-
import { type Detail } from '~/src/server/plugins/engine/models/types.js';
|
|
3
|
+
import { type Detail, type DetailItemField } from '~/src/server/plugins/engine/models/types.js';
|
|
3
4
|
import { type PageControllerClass } from '~/src/server/plugins/engine/pageControllers/helpers/pages.js';
|
|
4
|
-
import { type CheckAnswers, type FormContext, type FormContextRequest, type FormSubmissionError } from '~/src/server/plugins/engine/types.js';
|
|
5
|
+
import { type CheckAnswers, type FormContext, type FormContextRequest, type FormState, type FormSubmissionError } from '~/src/server/plugins/engine/types.js';
|
|
5
6
|
export declare class SummaryViewModel {
|
|
6
7
|
/**
|
|
7
8
|
* Responsible for parsing state values to the govuk-frontend summary list template
|
|
@@ -24,3 +25,11 @@ export declare class SummaryViewModel {
|
|
|
24
25
|
constructor(request: FormContextRequest, page: PageControllerClass, context: FormContext);
|
|
25
26
|
private summaryDetails;
|
|
26
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* Creates a form field detail item
|
|
30
|
+
* @see {@link DetailItemField}
|
|
31
|
+
*/
|
|
32
|
+
export declare function ItemField(page: PageControllerClass, state: FormState, field: Field, options: {
|
|
33
|
+
path: string;
|
|
34
|
+
errors?: FormSubmissionError[];
|
|
35
|
+
}): DetailItemField;
|
|
@@ -76,7 +76,7 @@ export class SummaryViewModel {
|
|
|
76
76
|
},
|
|
77
77
|
value: {
|
|
78
78
|
classes: 'app-prose-scope',
|
|
79
|
-
html: item.value || 'Not
|
|
79
|
+
html: item.value || 'Not provided'
|
|
80
80
|
},
|
|
81
81
|
actions: {
|
|
82
82
|
items
|
|
@@ -171,11 +171,11 @@ function ItemRepeat(page, state, options) {
|
|
|
171
171
|
* Creates a form field detail item
|
|
172
172
|
* @see {@link DetailItemField}
|
|
173
173
|
*/
|
|
174
|
-
function ItemField(page, state, field, options) {
|
|
174
|
+
export function ItemField(page, state, field, options) {
|
|
175
175
|
return {
|
|
176
176
|
name: field.name,
|
|
177
177
|
label: field.title,
|
|
178
|
-
title: field.label,
|
|
178
|
+
title: field.options.required === false ? `${field.label} (optional)` : field.label,
|
|
179
179
|
error: field.getFirstError(options.errors),
|
|
180
180
|
value: getAnswer(field, state),
|
|
181
181
|
href: getPageHref(page, options.path, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SummaryViewModel.js","names":["SchemaVersion","getAnswer","evaluateTemplate","getError","getPageHref","RepeatPageController","validationOptions","opts","SummaryViewModel","page","pageTitle","declaration","details","checkAnswers","context","name","backLink","feedbackLink","phaseTag","errors","serviceUrl","hasMissingNotificationEmail","components","allowSaveAndExit","constructor","request","model","basePath","def","sections","isForceAccess","title","schema","V2","result","makeFilteredSchema","relevantPages","validate","relevantState","stripUnknown","error","map","summaryDetails","detail","rows","items","item","push","href","text","classes","visuallyHiddenText","label","key","value","html","actions","undefined","summaryList","state","forEach","section","sectionPages","filter","collection","path","ItemRepeat","getSummaryPath","field","fields","ItemField","length","options","repeat","values","getListFromState","unit","returnUrl","subItems","repeatState","getFirstError"],"sources":["../../../../../src/server/plugins/engine/models/SummaryViewModel.ts"],"sourcesContent":["import { SchemaVersion, type Section } from '@defra/forms-model'\n\nimport {\n getAnswer,\n type Field\n} from '~/src/server/plugins/engine/components/helpers/components.js'\nimport {\n type BackLink,\n type ComponentViewModel\n} from '~/src/server/plugins/engine/components/types.js'\nimport {\n evaluateTemplate,\n getError,\n getPageHref\n} from '~/src/server/plugins/engine/helpers.js'\nimport {\n type Detail,\n type DetailItem,\n type DetailItemField,\n type DetailItemRepeat\n} from '~/src/server/plugins/engine/models/types.js'\nimport { RepeatPageController } from '~/src/server/plugins/engine/pageControllers/RepeatPageController.js'\nimport { type PageControllerClass } from '~/src/server/plugins/engine/pageControllers/helpers/pages.js'\nimport { validationOptions as opts } from '~/src/server/plugins/engine/pageControllers/validationOptions.js'\nimport {\n type CheckAnswers,\n type FormContext,\n type FormContextRequest,\n type FormState,\n type FormSubmissionError,\n type SummaryListAction,\n type SummaryListRow\n} from '~/src/server/plugins/engine/types.js'\n\nexport class SummaryViewModel {\n /**\n * Responsible for parsing state values to the govuk-frontend summary list template\n */\n\n page: PageControllerClass\n pageTitle: string\n declaration?: string\n details: Detail[]\n checkAnswers: CheckAnswers[]\n context: FormContext\n name: string | undefined\n backLink?: BackLink\n feedbackLink?: string\n phaseTag?: string\n errors?: FormSubmissionError[]\n serviceUrl: string\n hasMissingNotificationEmail?: boolean\n components?: ComponentViewModel[]\n allowSaveAndExit = false\n\n constructor(\n request: FormContextRequest,\n page: PageControllerClass,\n context: FormContext\n ) {\n const { model } = page\n const { basePath, def, sections } = model\n const { isForceAccess } = context\n\n this.page = page\n this.pageTitle = page.title\n if (def.schema === SchemaVersion.V2 && !page.title) {\n this.pageTitle = 'Check your answers before sending your form'\n }\n\n this.serviceUrl = `/${basePath}`\n this.name = def.name\n this.declaration = def.declaration\n this.context = context\n\n const result = model\n .makeFilteredSchema(this.context.relevantPages)\n .validate(this.context.relevantState, { ...opts, stripUnknown: true })\n\n // Format errors\n this.errors = result.error?.details.map(getError)\n this.details = this.summaryDetails(request, sections)\n\n // Format check answers\n this.checkAnswers = this.details.map((detail): CheckAnswers => {\n const { title } = detail\n\n const rows = detail.items.map((item): SummaryListRow => {\n const items: SummaryListAction[] = []\n\n // Remove summary list actions from previews\n if (!isForceAccess) {\n items.push({\n href: item.href,\n text: 'Change',\n classes: 'govuk-link--no-visited-state',\n visuallyHiddenText: item.label\n })\n }\n\n return {\n key: {\n text: evaluateTemplate(item.title, context)\n },\n value: {\n classes: 'app-prose-scope',\n html: item.value || 'Not supplied'\n },\n actions: {\n items\n }\n }\n })\n\n return {\n title: title ? { text: title } : undefined,\n summaryList: { rows }\n }\n })\n }\n\n private summaryDetails(request: FormContextRequest, sections: Section[]) {\n const { context, errors } = this\n const { relevantPages, state } = context\n\n const details: Detail[] = []\n\n ;[undefined, ...sections].forEach((section) => {\n const items: DetailItem[] = []\n\n const sectionPages = relevantPages.filter(\n (page) => page.section === section\n )\n\n sectionPages.forEach((page) => {\n const { collection, path } = page\n\n if (page instanceof RepeatPageController) {\n items.push(\n ItemRepeat(page, state, {\n path: page.getSummaryPath(request),\n errors\n })\n )\n } else {\n for (const field of collection.fields) {\n items.push(ItemField(page, state, field, { path, errors }))\n }\n }\n })\n\n if (items.length) {\n details.push({\n name: section?.name,\n title: section?.title,\n items\n })\n }\n })\n\n return details\n }\n}\n\n/**\n * Creates a repeater detail item\n * @see {@link DetailItemField}\n */\nfunction ItemRepeat(\n page: RepeatPageController,\n state: FormState,\n options: {\n path: string\n errors?: FormSubmissionError[]\n }\n): DetailItemRepeat {\n const { collection, repeat } = page\n const { name, title } = repeat.options\n\n const values = page.getListFromState(state)\n const unit = values.length === 1 ? title : `${title}s`\n\n return {\n name,\n label: title,\n title: values.length ? `${unit} added` : unit,\n value: values.length ? `You added ${values.length} ${unit}` : '',\n href: getPageHref(page, options.path, {\n returnUrl: getPageHref(page, page.getSummaryPath())\n }),\n state,\n page,\n\n // Repeater field detail items\n subItems: values.map((repeatState) =>\n collection.fields.map((field) =>\n ItemField(page, repeatState, field, options)\n )\n )\n }\n}\n\n/**\n * Creates a form field detail item\n * @see {@link DetailItemField}\n */\nfunction ItemField(\n page: PageControllerClass,\n state: FormState,\n field: Field,\n options: {\n path: string\n errors?: FormSubmissionError[]\n }\n): DetailItemField {\n return {\n name: field.name,\n label: field.title,\n title: field.label,\n error: field.getFirstError(options.errors),\n value: getAnswer(field, state),\n href: getPageHref(page, options.path, {\n returnUrl: getPageHref(page, page.getSummaryPath())\n }),\n state,\n page,\n field\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"SummaryViewModel.js","names":["SchemaVersion","getAnswer","evaluateTemplate","getError","getPageHref","RepeatPageController","validationOptions","opts","SummaryViewModel","page","pageTitle","declaration","details","checkAnswers","context","name","backLink","feedbackLink","phaseTag","errors","serviceUrl","hasMissingNotificationEmail","components","allowSaveAndExit","constructor","request","model","basePath","def","sections","isForceAccess","title","schema","V2","result","makeFilteredSchema","relevantPages","validate","relevantState","stripUnknown","error","map","summaryDetails","detail","rows","items","item","push","href","text","classes","visuallyHiddenText","label","key","value","html","actions","undefined","summaryList","state","forEach","section","sectionPages","filter","collection","path","ItemRepeat","getSummaryPath","field","fields","ItemField","length","options","repeat","values","getListFromState","unit","returnUrl","subItems","repeatState","required","getFirstError"],"sources":["../../../../../src/server/plugins/engine/models/SummaryViewModel.ts"],"sourcesContent":["import { SchemaVersion, type Section } from '@defra/forms-model'\n\nimport {\n getAnswer,\n type Field\n} from '~/src/server/plugins/engine/components/helpers/components.js'\nimport {\n type BackLink,\n type ComponentViewModel\n} from '~/src/server/plugins/engine/components/types.js'\nimport {\n evaluateTemplate,\n getError,\n getPageHref\n} from '~/src/server/plugins/engine/helpers.js'\nimport {\n type Detail,\n type DetailItem,\n type DetailItemField,\n type DetailItemRepeat\n} from '~/src/server/plugins/engine/models/types.js'\nimport { RepeatPageController } from '~/src/server/plugins/engine/pageControllers/RepeatPageController.js'\nimport { type PageControllerClass } from '~/src/server/plugins/engine/pageControllers/helpers/pages.js'\nimport { validationOptions as opts } from '~/src/server/plugins/engine/pageControllers/validationOptions.js'\nimport {\n type CheckAnswers,\n type FormContext,\n type FormContextRequest,\n type FormState,\n type FormSubmissionError,\n type SummaryListAction,\n type SummaryListRow\n} from '~/src/server/plugins/engine/types.js'\n\nexport class SummaryViewModel {\n /**\n * Responsible for parsing state values to the govuk-frontend summary list template\n */\n\n page: PageControllerClass\n pageTitle: string\n declaration?: string\n details: Detail[]\n checkAnswers: CheckAnswers[]\n context: FormContext\n name: string | undefined\n backLink?: BackLink\n feedbackLink?: string\n phaseTag?: string\n errors?: FormSubmissionError[]\n serviceUrl: string\n hasMissingNotificationEmail?: boolean\n components?: ComponentViewModel[]\n allowSaveAndExit = false\n\n constructor(\n request: FormContextRequest,\n page: PageControllerClass,\n context: FormContext\n ) {\n const { model } = page\n const { basePath, def, sections } = model\n const { isForceAccess } = context\n\n this.page = page\n this.pageTitle = page.title\n if (def.schema === SchemaVersion.V2 && !page.title) {\n this.pageTitle = 'Check your answers before sending your form'\n }\n\n this.serviceUrl = `/${basePath}`\n this.name = def.name\n this.declaration = def.declaration\n this.context = context\n\n const result = model\n .makeFilteredSchema(this.context.relevantPages)\n .validate(this.context.relevantState, { ...opts, stripUnknown: true })\n\n // Format errors\n this.errors = result.error?.details.map(getError)\n this.details = this.summaryDetails(request, sections)\n\n // Format check answers\n this.checkAnswers = this.details.map((detail): CheckAnswers => {\n const { title } = detail\n\n const rows = detail.items.map((item): SummaryListRow => {\n const items: SummaryListAction[] = []\n\n // Remove summary list actions from previews\n if (!isForceAccess) {\n items.push({\n href: item.href,\n text: 'Change',\n classes: 'govuk-link--no-visited-state',\n visuallyHiddenText: item.label\n })\n }\n\n return {\n key: {\n text: evaluateTemplate(item.title, context)\n },\n value: {\n classes: 'app-prose-scope',\n html: item.value || 'Not provided'\n },\n actions: {\n items\n }\n }\n })\n\n return {\n title: title ? { text: title } : undefined,\n summaryList: { rows }\n }\n })\n }\n\n private summaryDetails(request: FormContextRequest, sections: Section[]) {\n const { context, errors } = this\n const { relevantPages, state } = context\n\n const details: Detail[] = []\n\n ;[undefined, ...sections].forEach((section) => {\n const items: DetailItem[] = []\n\n const sectionPages = relevantPages.filter(\n (page) => page.section === section\n )\n\n sectionPages.forEach((page) => {\n const { collection, path } = page\n\n if (page instanceof RepeatPageController) {\n items.push(\n ItemRepeat(page, state, {\n path: page.getSummaryPath(request),\n errors\n })\n )\n } else {\n for (const field of collection.fields) {\n items.push(ItemField(page, state, field, { path, errors }))\n }\n }\n })\n\n if (items.length) {\n details.push({\n name: section?.name,\n title: section?.title,\n items\n })\n }\n })\n\n return details\n }\n}\n\n/**\n * Creates a repeater detail item\n * @see {@link DetailItemField}\n */\nfunction ItemRepeat(\n page: RepeatPageController,\n state: FormState,\n options: {\n path: string\n errors?: FormSubmissionError[]\n }\n): DetailItemRepeat {\n const { collection, repeat } = page\n const { name, title } = repeat.options\n\n const values = page.getListFromState(state)\n const unit = values.length === 1 ? title : `${title}s`\n\n return {\n name,\n label: title,\n title: values.length ? `${unit} added` : unit,\n value: values.length ? `You added ${values.length} ${unit}` : '',\n href: getPageHref(page, options.path, {\n returnUrl: getPageHref(page, page.getSummaryPath())\n }),\n state,\n page,\n\n // Repeater field detail items\n subItems: values.map((repeatState) =>\n collection.fields.map((field) =>\n ItemField(page, repeatState, field, options)\n )\n )\n }\n}\n\n/**\n * Creates a form field detail item\n * @see {@link DetailItemField}\n */\nexport function ItemField(\n page: PageControllerClass,\n state: FormState,\n field: Field,\n options: {\n path: string\n errors?: FormSubmissionError[]\n }\n): DetailItemField {\n return {\n name: field.name,\n label: field.title,\n title:\n field.options.required === false\n ? `${field.label} (optional)`\n : field.label,\n error: field.getFirstError(options.errors),\n value: getAnswer(field, state),\n href: getPageHref(page, options.path, {\n returnUrl: getPageHref(page, page.getSummaryPath())\n }),\n state,\n page,\n field\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RepeatPageController.js","names":["randomUUID","Boom","Joi","isRepeatState","redirectPath","QuestionPageController","FormAction","RepeatPageController","listSummaryViewName","listDeleteViewName","repeat","allowSaveAndExit","constructor","model","pageDef","options","schema","itemId","string","uuid","required","collection","formSchema","append","stateSchema","object","keys","name","array","items","min","max","label","title","getFormParams","request","params","payload","getFormDataFromState","state","list","getListFromState","getItemId","item","getItemFromList","getStateFromValidForm","badRequest","itemState","updated","newList","push","indexOf","proceed","h","nextPath","getSummaryPath","find","values","makeGetRouteHandler","context","path","query","summaryPath","returnUrl","force","length","makeGetListSummaryRouteHandler","viewModel","getListSummaryViewModel","view","makePostListSummaryRouteHandler","action","hasErrorMin","Continue","hasErrorMax","AddAnother","count","itemTitle","errors","href","text","SaveAndExit","handleSaveAndExit","getNextPath","makeGetItemDeleteRouteHandler","notFound","backLink","getBackLink","pageTitle","buttonConfirm","buttonCancel","makePostItemDeleteRouteHandler","confirm","splice","update","mergeState","getViewModel","itemNumber","repeatCaption","sectionTitle","isForceAccess","summaryList","classes","rows","Array","isArray","forEach","index","getHref","visuallyHiddenText","itemDisplayText","fields","getDisplayStringFromState","key","value","actions","repeatTitle","showTitle","checkAnswers","shouldShowSaveAndExit","server"],"sources":["../../../../../src/server/plugins/engine/pageControllers/RepeatPageController.ts"],"sourcesContent":["import { randomUUID } from 'crypto'\n\nimport { type PageRepeat, type Repeat } from '@defra/forms-model'\nimport Boom from '@hapi/boom'\nimport Joi from 'joi'\n\nimport { isRepeatState } from '~/src/server/plugins/engine/components/FormComponent.js'\nimport { redirectPath } from '~/src/server/plugins/engine/helpers.js'\nimport { type FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport { QuestionPageController } from '~/src/server/plugins/engine/pageControllers/QuestionPageController.js'\nimport {\n type FormContext,\n type FormContextRequest,\n type FormPageViewModel,\n type FormPayload,\n type FormSubmissionState,\n type ItemDeletePageViewModel,\n type RepeatItemState,\n type RepeatListState,\n type RepeaterSummaryPageViewModel,\n type SummaryList,\n type SummaryListAction\n} from '~/src/server/plugins/engine/types.js'\nimport {\n FormAction,\n type FormRequest,\n type FormRequestPayload,\n type FormResponseToolkit\n} from '~/src/server/routes/types.js'\n\nexport class RepeatPageController extends QuestionPageController {\n declare pageDef: PageRepeat\n\n listSummaryViewName = 'repeat-list-summary'\n listDeleteViewName = 'item-delete'\n repeat: Repeat\n allowSaveAndExit = true\n\n constructor(model: FormModel, pageDef: PageRepeat) {\n super(model, pageDef)\n\n this.repeat = pageDef.repeat\n\n const { options, schema } = this.repeat\n const itemId = Joi.string().uuid().required()\n\n this.collection.formSchema = this.collection.formSchema.append({ itemId })\n this.collection.stateSchema = Joi.object<RepeatItemState>().keys({\n [options.name]: Joi.array()\n .items(this.collection.stateSchema.append({ itemId }))\n .min(schema.min)\n .max(schema.max)\n .label(`${options.title} list`)\n .required()\n })\n }\n\n get keys() {\n const { repeat } = this\n return [repeat.options.name, ...super.keys]\n }\n\n getFormParams(request?: FormContextRequest) {\n const params = super.getFormParams(request)\n\n // Apply an itemId to the form payload\n if (request?.payload) {\n params.itemId = request.params.itemId ?? randomUUID()\n }\n\n return params\n }\n\n getFormDataFromState(\n request: FormContextRequest | undefined,\n state: FormSubmissionState\n ) {\n const { repeat } = this\n\n const params = this.getFormParams(request)\n const list = this.getListFromState(state)\n const itemId = this.getItemId(request)\n\n // Create payload with repeater list state\n if (!itemId) {\n return {\n ...params,\n [repeat.options.name]: list\n }\n }\n\n // Create payload with repeater item state\n const item = this.getItemFromList(list, itemId)\n\n return {\n ...params,\n ...item\n }\n }\n\n getStateFromValidForm(\n request: FormContextRequest,\n state: FormSubmissionState,\n payload: FormPayload\n ) {\n const itemId = this.getItemId(request)\n\n if (!itemId) {\n throw Boom.badRequest('No item ID found')\n }\n\n const list = this.getListFromState(state)\n const item = this.getItemFromList(list, itemId)\n\n const itemState = super.getStateFromValidForm(request, state, payload)\n const updated: RepeatItemState = { ...itemState, itemId }\n const newList = [...list]\n\n if (!item) {\n // Adding a new item\n newList.push(updated)\n } else {\n // Update an existing item\n newList[list.indexOf(item)] = updated\n }\n\n return {\n [this.repeat.options.name]: newList\n }\n }\n\n proceed(request: FormContextRequest, h: FormResponseToolkit) {\n const nextPath = this.getSummaryPath(request)\n return super.proceed(request, h, nextPath)\n }\n\n getItemFromList(list: RepeatListState, itemId?: string) {\n return list.find((item) => item.itemId === itemId)\n }\n\n getListFromState(state: FormSubmissionState) {\n const { name } = this.repeat.options\n const values = state[name]\n\n return isRepeatState(values) ? values : []\n }\n\n makeGetRouteHandler() {\n return async (\n request: FormRequest,\n context: FormContext,\n h: FormResponseToolkit\n ) => {\n const { path } = this\n const { query } = request\n const { state } = context\n\n const itemId = this.getItemId(request)\n const list = this.getListFromState(state)\n\n if (!itemId) {\n const summaryPath = this.getSummaryPath(request)\n const nextPath = redirectPath(`${path}/${randomUUID()}`, {\n returnUrl: query.returnUrl,\n force: query.force\n })\n\n // Only redirect to new item when list is empty\n return super.proceed(request, h, list.length ? summaryPath : nextPath)\n }\n\n return super.makeGetRouteHandler()(request, context, h)\n }\n }\n\n makeGetListSummaryRouteHandler() {\n return (\n request: FormRequest,\n context: FormContext,\n h: FormResponseToolkit\n ) => {\n const { path } = this\n const { query } = request\n const { state } = context\n\n const list = this.getListFromState(state)\n\n if (!list.length) {\n const nextPath = redirectPath(`${path}/${randomUUID()}`, {\n returnUrl: query.returnUrl\n })\n\n return super.proceed(request, h, nextPath)\n }\n\n const viewModel = this.getListSummaryViewModel(request, context, list)\n\n return h.view(this.listSummaryViewName, viewModel)\n }\n }\n\n makePostListSummaryRouteHandler() {\n return (\n request: FormRequestPayload,\n context: FormContext,\n h: FormResponseToolkit\n ) => {\n const { path, repeat } = this\n const { query } = request\n const { schema, options } = repeat\n const { state } = context\n\n const list = this.getListFromState(state)\n\n if (!list.length) {\n const nextPath = redirectPath(`${path}/${randomUUID()}`, {\n returnUrl: query.returnUrl\n })\n\n return super.proceed(request, h, nextPath)\n }\n\n const { action } = this.getFormParams(request)\n\n const hasErrorMin =\n action === FormAction.Continue && list.length < schema.min\n\n const hasErrorMax =\n (action === FormAction.AddAnother && list.length >= schema.max) ||\n (action === FormAction.Continue && list.length > schema.max)\n\n // Show error if repeat limits apply\n if (hasErrorMin || hasErrorMax) {\n const count = hasErrorMax ? schema.max : schema.min\n const itemTitle = `${options.title}${count === 1 ? '' : 's'}`\n\n context.errors = [\n {\n path: [],\n href: '',\n name: '',\n text: hasErrorMax\n ? `You can only add up to ${count} ${itemTitle}`\n : `You must add at least ${count} ${itemTitle}`\n }\n ]\n\n const viewModel = this.getListSummaryViewModel(request, context, list)\n\n return h.view(this.listSummaryViewName, viewModel)\n }\n\n if (action === FormAction.AddAnother) {\n const nextPath = redirectPath(`${path}/${randomUUID()}`, {\n returnUrl: query.returnUrl\n })\n\n return super.proceed(request, h, nextPath)\n }\n\n // Check if this is a save-and-exit action\n if (action === FormAction.SaveAndExit) {\n return this.handleSaveAndExit(request, context, h)\n }\n\n const nextPath = this.getNextPath(context)\n return super.proceed(request, h, nextPath)\n }\n }\n\n makeGetItemDeleteRouteHandler() {\n return (\n request: FormRequest,\n context: FormContext,\n h: FormResponseToolkit\n ) => {\n const { viewModel } = this\n const { state } = context\n\n const list = this.getListFromState(state)\n const itemId = this.getItemId(request)\n const item = this.getItemFromList(list, itemId)\n\n if (!item || list.length === 1) {\n throw Boom.notFound(\n item\n ? 'Last list item cannot be removed'\n : 'List item to remove not found'\n )\n }\n\n const { title } = this.repeat.options\n\n return h.view(this.listDeleteViewName, {\n ...viewModel,\n context,\n backLink: this.getBackLink(request, context),\n pageTitle: `Are you sure you want to remove this ${title}?`,\n itemTitle: `${title} ${list.indexOf(item) + 1}`,\n buttonConfirm: { text: `Remove ${title}` },\n buttonCancel: { text: 'Cancel' }\n } satisfies ItemDeletePageViewModel)\n }\n }\n\n makePostItemDeleteRouteHandler() {\n return async (\n request: FormRequestPayload,\n context: FormContext,\n h: FormResponseToolkit\n ) => {\n const { repeat } = this\n const { state } = context\n\n const { confirm } = this.getFormParams(request)\n\n const list = this.getListFromState(state)\n const itemId = this.getItemId(request)\n const item = this.getItemFromList(list, itemId)\n\n if (!item || list.length === 1) {\n throw Boom.notFound(\n item\n ? 'Last list item cannot be removed'\n : 'List item to remove not found'\n )\n }\n\n // Remove the item from the list\n if (confirm) {\n list.splice(list.indexOf(item), 1)\n\n const update = {\n [repeat.options.name]: list\n }\n\n await this.mergeState(request, state, update)\n }\n\n return this.proceed(request, h)\n }\n }\n\n getViewModel(\n request: FormContextRequest,\n context: FormContext\n ): FormPageViewModel {\n const { state } = context\n\n const list = this.getListFromState(state)\n const itemId = this.getItemId(request)\n const item = this.getItemFromList(list, itemId)\n\n const viewModel = super.getViewModel(request, context)\n const itemNumber = item ? list.indexOf(item) + 1 : list.length + 1\n const repeatCaption = `${this.repeat.options.title} ${itemNumber}`\n\n return {\n ...viewModel,\n\n sectionTitle: viewModel.sectionTitle\n ? `${viewModel.sectionTitle}: ${repeatCaption}`\n : repeatCaption\n }\n }\n\n getListSummaryViewModel(\n request: FormContextRequest,\n context: FormContext,\n list: RepeatListState\n ): RepeaterSummaryPageViewModel {\n const { collection, href, repeat } = this\n const { query } = request\n const { isForceAccess, errors } = context\n\n const { title } = repeat.options\n\n const summaryList: SummaryList = {\n classes: 'govuk-summary-list--long-actions',\n rows: []\n }\n\n let count = 0\n\n if (Array.isArray(list)) {\n count = list.length\n\n const summaryPath = this.getSummaryPath(request)\n\n list.forEach((item, index) => {\n const items: SummaryListAction[] = []\n\n // Remove summary list actions from previews\n if (!isForceAccess) {\n items.push({\n href: redirectPath(`${href}/${item.itemId}`, {\n returnUrl: query.returnUrl ?? this.getHref(summaryPath)\n }),\n text: 'Change',\n classes: 'govuk-link--no-visited-state',\n visuallyHiddenText: `item ${index + 1}`\n })\n\n if (count > 1) {\n items.push({\n href: redirectPath(`${href}/${item.itemId}/confirm-delete`, {\n returnUrl: query.returnUrl\n }),\n text: 'Remove',\n classes: 'govuk-link--no-visited-state',\n visuallyHiddenText: `item ${index + 1}`\n })\n }\n }\n\n const itemDisplayText = collection.fields.length\n ? collection.fields[0].getDisplayStringFromState(item)\n : ''\n\n summaryList.rows.push({\n key: {\n text: `${title} ${index + 1}`\n },\n value: {\n text: itemDisplayText || 'Not supplied'\n },\n actions: {\n items\n }\n })\n })\n }\n\n return {\n ...this.viewModel,\n backLink: this.getBackLink(request, context),\n repeatTitle: title,\n pageTitle: `You have added ${count} ${title}${count === 1 ? '' : 's'}`,\n showTitle: true,\n context,\n errors,\n checkAnswers: [{ summaryList }],\n allowSaveAndExit: this.shouldShowSaveAndExit(request.server)\n }\n }\n\n getSummaryPath(request?: FormContextRequest) {\n const { path } = this\n\n const summaryPath = super.getSummaryPath()\n\n if (!request) {\n return summaryPath\n }\n\n const { query } = request\n\n return redirectPath(`${path}${summaryPath}`, {\n returnUrl: query.returnUrl\n })\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"RepeatPageController.js","names":["randomUUID","Boom","Joi","isRepeatState","redirectPath","QuestionPageController","FormAction","RepeatPageController","listSummaryViewName","listDeleteViewName","repeat","allowSaveAndExit","constructor","model","pageDef","options","schema","itemId","string","uuid","required","collection","formSchema","append","stateSchema","object","keys","name","array","items","min","max","label","title","getFormParams","request","params","payload","getFormDataFromState","state","list","getListFromState","getItemId","item","getItemFromList","getStateFromValidForm","badRequest","itemState","updated","newList","push","indexOf","proceed","h","nextPath","getSummaryPath","find","values","makeGetRouteHandler","context","path","query","summaryPath","returnUrl","force","length","makeGetListSummaryRouteHandler","viewModel","getListSummaryViewModel","view","makePostListSummaryRouteHandler","action","hasErrorMin","Continue","hasErrorMax","AddAnother","count","itemTitle","errors","href","text","SaveAndExit","handleSaveAndExit","getNextPath","makeGetItemDeleteRouteHandler","notFound","backLink","getBackLink","pageTitle","buttonConfirm","buttonCancel","makePostItemDeleteRouteHandler","confirm","splice","update","mergeState","getViewModel","itemNumber","repeatCaption","sectionTitle","isForceAccess","summaryList","classes","rows","Array","isArray","forEach","index","getHref","visuallyHiddenText","itemDisplayText","fields","getDisplayStringFromState","key","value","actions","repeatTitle","showTitle","checkAnswers","shouldShowSaveAndExit","server"],"sources":["../../../../../src/server/plugins/engine/pageControllers/RepeatPageController.ts"],"sourcesContent":["import { randomUUID } from 'crypto'\n\nimport { type PageRepeat, type Repeat } from '@defra/forms-model'\nimport Boom from '@hapi/boom'\nimport Joi from 'joi'\n\nimport { isRepeatState } from '~/src/server/plugins/engine/components/FormComponent.js'\nimport { redirectPath } from '~/src/server/plugins/engine/helpers.js'\nimport { type FormModel } from '~/src/server/plugins/engine/models/index.js'\nimport { QuestionPageController } from '~/src/server/plugins/engine/pageControllers/QuestionPageController.js'\nimport {\n type FormContext,\n type FormContextRequest,\n type FormPageViewModel,\n type FormPayload,\n type FormSubmissionState,\n type ItemDeletePageViewModel,\n type RepeatItemState,\n type RepeatListState,\n type RepeaterSummaryPageViewModel,\n type SummaryList,\n type SummaryListAction\n} from '~/src/server/plugins/engine/types.js'\nimport {\n FormAction,\n type FormRequest,\n type FormRequestPayload,\n type FormResponseToolkit\n} from '~/src/server/routes/types.js'\n\nexport class RepeatPageController extends QuestionPageController {\n declare pageDef: PageRepeat\n\n listSummaryViewName = 'repeat-list-summary'\n listDeleteViewName = 'item-delete'\n repeat: Repeat\n allowSaveAndExit = true\n\n constructor(model: FormModel, pageDef: PageRepeat) {\n super(model, pageDef)\n\n this.repeat = pageDef.repeat\n\n const { options, schema } = this.repeat\n const itemId = Joi.string().uuid().required()\n\n this.collection.formSchema = this.collection.formSchema.append({ itemId })\n this.collection.stateSchema = Joi.object<RepeatItemState>().keys({\n [options.name]: Joi.array()\n .items(this.collection.stateSchema.append({ itemId }))\n .min(schema.min)\n .max(schema.max)\n .label(`${options.title} list`)\n .required()\n })\n }\n\n get keys() {\n const { repeat } = this\n return [repeat.options.name, ...super.keys]\n }\n\n getFormParams(request?: FormContextRequest) {\n const params = super.getFormParams(request)\n\n // Apply an itemId to the form payload\n if (request?.payload) {\n params.itemId = request.params.itemId ?? randomUUID()\n }\n\n return params\n }\n\n getFormDataFromState(\n request: FormContextRequest | undefined,\n state: FormSubmissionState\n ) {\n const { repeat } = this\n\n const params = this.getFormParams(request)\n const list = this.getListFromState(state)\n const itemId = this.getItemId(request)\n\n // Create payload with repeater list state\n if (!itemId) {\n return {\n ...params,\n [repeat.options.name]: list\n }\n }\n\n // Create payload with repeater item state\n const item = this.getItemFromList(list, itemId)\n\n return {\n ...params,\n ...item\n }\n }\n\n getStateFromValidForm(\n request: FormContextRequest,\n state: FormSubmissionState,\n payload: FormPayload\n ) {\n const itemId = this.getItemId(request)\n\n if (!itemId) {\n throw Boom.badRequest('No item ID found')\n }\n\n const list = this.getListFromState(state)\n const item = this.getItemFromList(list, itemId)\n\n const itemState = super.getStateFromValidForm(request, state, payload)\n const updated: RepeatItemState = { ...itemState, itemId }\n const newList = [...list]\n\n if (!item) {\n // Adding a new item\n newList.push(updated)\n } else {\n // Update an existing item\n newList[list.indexOf(item)] = updated\n }\n\n return {\n [this.repeat.options.name]: newList\n }\n }\n\n proceed(request: FormContextRequest, h: FormResponseToolkit) {\n const nextPath = this.getSummaryPath(request)\n return super.proceed(request, h, nextPath)\n }\n\n getItemFromList(list: RepeatListState, itemId?: string) {\n return list.find((item) => item.itemId === itemId)\n }\n\n getListFromState(state: FormSubmissionState) {\n const { name } = this.repeat.options\n const values = state[name]\n\n return isRepeatState(values) ? values : []\n }\n\n makeGetRouteHandler() {\n return async (\n request: FormRequest,\n context: FormContext,\n h: FormResponseToolkit\n ) => {\n const { path } = this\n const { query } = request\n const { state } = context\n\n const itemId = this.getItemId(request)\n const list = this.getListFromState(state)\n\n if (!itemId) {\n const summaryPath = this.getSummaryPath(request)\n const nextPath = redirectPath(`${path}/${randomUUID()}`, {\n returnUrl: query.returnUrl,\n force: query.force\n })\n\n // Only redirect to new item when list is empty\n return super.proceed(request, h, list.length ? summaryPath : nextPath)\n }\n\n return super.makeGetRouteHandler()(request, context, h)\n }\n }\n\n makeGetListSummaryRouteHandler() {\n return (\n request: FormRequest,\n context: FormContext,\n h: FormResponseToolkit\n ) => {\n const { path } = this\n const { query } = request\n const { state } = context\n\n const list = this.getListFromState(state)\n\n if (!list.length) {\n const nextPath = redirectPath(`${path}/${randomUUID()}`, {\n returnUrl: query.returnUrl\n })\n\n return super.proceed(request, h, nextPath)\n }\n\n const viewModel = this.getListSummaryViewModel(request, context, list)\n\n return h.view(this.listSummaryViewName, viewModel)\n }\n }\n\n makePostListSummaryRouteHandler() {\n return (\n request: FormRequestPayload,\n context: FormContext,\n h: FormResponseToolkit\n ) => {\n const { path, repeat } = this\n const { query } = request\n const { schema, options } = repeat\n const { state } = context\n\n const list = this.getListFromState(state)\n\n if (!list.length) {\n const nextPath = redirectPath(`${path}/${randomUUID()}`, {\n returnUrl: query.returnUrl\n })\n\n return super.proceed(request, h, nextPath)\n }\n\n const { action } = this.getFormParams(request)\n\n const hasErrorMin =\n action === FormAction.Continue && list.length < schema.min\n\n const hasErrorMax =\n (action === FormAction.AddAnother && list.length >= schema.max) ||\n (action === FormAction.Continue && list.length > schema.max)\n\n // Show error if repeat limits apply\n if (hasErrorMin || hasErrorMax) {\n const count = hasErrorMax ? schema.max : schema.min\n const itemTitle = `${options.title}${count === 1 ? '' : 's'}`\n\n context.errors = [\n {\n path: [],\n href: '',\n name: '',\n text: hasErrorMax\n ? `You can only add up to ${count} ${itemTitle}`\n : `You must add at least ${count} ${itemTitle}`\n }\n ]\n\n const viewModel = this.getListSummaryViewModel(request, context, list)\n\n return h.view(this.listSummaryViewName, viewModel)\n }\n\n if (action === FormAction.AddAnother) {\n const nextPath = redirectPath(`${path}/${randomUUID()}`, {\n returnUrl: query.returnUrl\n })\n\n return super.proceed(request, h, nextPath)\n }\n\n // Check if this is a save-and-exit action\n if (action === FormAction.SaveAndExit) {\n return this.handleSaveAndExit(request, context, h)\n }\n\n const nextPath = this.getNextPath(context)\n return super.proceed(request, h, nextPath)\n }\n }\n\n makeGetItemDeleteRouteHandler() {\n return (\n request: FormRequest,\n context: FormContext,\n h: FormResponseToolkit\n ) => {\n const { viewModel } = this\n const { state } = context\n\n const list = this.getListFromState(state)\n const itemId = this.getItemId(request)\n const item = this.getItemFromList(list, itemId)\n\n if (!item || list.length === 1) {\n throw Boom.notFound(\n item\n ? 'Last list item cannot be removed'\n : 'List item to remove not found'\n )\n }\n\n const { title } = this.repeat.options\n\n return h.view(this.listDeleteViewName, {\n ...viewModel,\n context,\n backLink: this.getBackLink(request, context),\n pageTitle: `Are you sure you want to remove this ${title}?`,\n itemTitle: `${title} ${list.indexOf(item) + 1}`,\n buttonConfirm: { text: `Remove ${title}` },\n buttonCancel: { text: 'Cancel' }\n } satisfies ItemDeletePageViewModel)\n }\n }\n\n makePostItemDeleteRouteHandler() {\n return async (\n request: FormRequestPayload,\n context: FormContext,\n h: FormResponseToolkit\n ) => {\n const { repeat } = this\n const { state } = context\n\n const { confirm } = this.getFormParams(request)\n\n const list = this.getListFromState(state)\n const itemId = this.getItemId(request)\n const item = this.getItemFromList(list, itemId)\n\n if (!item || list.length === 1) {\n throw Boom.notFound(\n item\n ? 'Last list item cannot be removed'\n : 'List item to remove not found'\n )\n }\n\n // Remove the item from the list\n if (confirm) {\n list.splice(list.indexOf(item), 1)\n\n const update = {\n [repeat.options.name]: list\n }\n\n await this.mergeState(request, state, update)\n }\n\n return this.proceed(request, h)\n }\n }\n\n getViewModel(\n request: FormContextRequest,\n context: FormContext\n ): FormPageViewModel {\n const { state } = context\n\n const list = this.getListFromState(state)\n const itemId = this.getItemId(request)\n const item = this.getItemFromList(list, itemId)\n\n const viewModel = super.getViewModel(request, context)\n const itemNumber = item ? list.indexOf(item) + 1 : list.length + 1\n const repeatCaption = `${this.repeat.options.title} ${itemNumber}`\n\n return {\n ...viewModel,\n\n sectionTitle: viewModel.sectionTitle\n ? `${viewModel.sectionTitle}: ${repeatCaption}`\n : repeatCaption\n }\n }\n\n getListSummaryViewModel(\n request: FormContextRequest,\n context: FormContext,\n list: RepeatListState\n ): RepeaterSummaryPageViewModel {\n const { collection, href, repeat } = this\n const { query } = request\n const { isForceAccess, errors } = context\n\n const { title } = repeat.options\n\n const summaryList: SummaryList = {\n classes: 'govuk-summary-list--long-actions',\n rows: []\n }\n\n let count = 0\n\n if (Array.isArray(list)) {\n count = list.length\n\n const summaryPath = this.getSummaryPath(request)\n\n list.forEach((item, index) => {\n const items: SummaryListAction[] = []\n\n // Remove summary list actions from previews\n if (!isForceAccess) {\n items.push({\n href: redirectPath(`${href}/${item.itemId}`, {\n returnUrl: query.returnUrl ?? this.getHref(summaryPath)\n }),\n text: 'Change',\n classes: 'govuk-link--no-visited-state',\n visuallyHiddenText: `item ${index + 1}`\n })\n\n if (count > 1) {\n items.push({\n href: redirectPath(`${href}/${item.itemId}/confirm-delete`, {\n returnUrl: query.returnUrl\n }),\n text: 'Remove',\n classes: 'govuk-link--no-visited-state',\n visuallyHiddenText: `item ${index + 1}`\n })\n }\n }\n\n const itemDisplayText = collection.fields.length\n ? collection.fields[0].getDisplayStringFromState(item)\n : ''\n\n summaryList.rows.push({\n key: {\n text: `${title} ${index + 1}`\n },\n value: {\n text: itemDisplayText || 'Not provided'\n },\n actions: {\n items\n }\n })\n })\n }\n\n return {\n ...this.viewModel,\n backLink: this.getBackLink(request, context),\n repeatTitle: title,\n pageTitle: `You have added ${count} ${title}${count === 1 ? '' : 's'}`,\n showTitle: true,\n context,\n errors,\n checkAnswers: [{ summaryList }],\n allowSaveAndExit: this.shouldShowSaveAndExit(request.server)\n }\n }\n\n getSummaryPath(request?: FormContextRequest) {\n const { path } = this\n\n const summaryPath = super.getSummaryPath()\n\n if (!request) {\n return summaryPath\n }\n\n const { query } = request\n\n return redirectPath(`${path}${summaryPath}`, {\n returnUrl: query.returnUrl\n })\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@defra/forms-engine-plugin",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.16",
|
|
4
4
|
"description": "Defra forms engine",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
},
|
|
71
71
|
"license": "SEE LICENSE IN LICENSE",
|
|
72
72
|
"dependencies": {
|
|
73
|
-
"@defra/forms-model": "^3.0.
|
|
73
|
+
"@defra/forms-model": "^3.0.579",
|
|
74
74
|
"@defra/hapi-tracing": "^1.26.0",
|
|
75
75
|
"@elastic/ecs-pino-format": "^1.5.0",
|
|
76
76
|
"@hapi/boom": "^10.0.1",
|
|
@@ -186,7 +186,7 @@ describe('DeclarationField', () => {
|
|
|
186
186
|
const answer2 = getAnswer(field, state2)
|
|
187
187
|
|
|
188
188
|
expect(answer1).toBe('I understand and agree')
|
|
189
|
-
expect(answer2).toBe('')
|
|
189
|
+
expect(answer2).toBe('Not provided')
|
|
190
190
|
})
|
|
191
191
|
|
|
192
192
|
it('returns payload from state', () => {
|
|
@@ -197,7 +197,7 @@ describe('DeclarationField', () => {
|
|
|
197
197
|
const payload2 = field.getFormDataFromState(state2)
|
|
198
198
|
|
|
199
199
|
expect(payload1).toEqual(getFormData('true'))
|
|
200
|
-
expect(payload2).toEqual(getFormData())
|
|
200
|
+
expect(payload2).toEqual(getFormData('unchecked'))
|
|
201
201
|
})
|
|
202
202
|
|
|
203
203
|
it('returns value from state', () => {
|
|
@@ -208,7 +208,7 @@ describe('DeclarationField', () => {
|
|
|
208
208
|
const value2 = field.getFormValueFromState(state2)
|
|
209
209
|
|
|
210
210
|
expect(value1).toBe('true')
|
|
211
|
-
expect(value2).
|
|
211
|
+
expect(value2).toBe('unchecked')
|
|
212
212
|
})
|
|
213
213
|
|
|
214
214
|
it('returns context for conditions and form submission', () => {
|
|
@@ -239,14 +239,13 @@ describe('DeclarationField', () => {
|
|
|
239
239
|
|
|
240
240
|
describe('View model', () => {
|
|
241
241
|
it('sets Nunjucks component defaults', () => {
|
|
242
|
-
const viewModel = field.getViewModel(getFormData(
|
|
242
|
+
const viewModel = field.getViewModel(getFormData(undefined))
|
|
243
243
|
|
|
244
244
|
expect(viewModel).toEqual(
|
|
245
245
|
expect.objectContaining({
|
|
246
246
|
label: { text: def.title },
|
|
247
247
|
name: 'myComponent',
|
|
248
248
|
attributes: {},
|
|
249
|
-
values: [],
|
|
250
249
|
content: 'Lorem ipsum dolar sit amet',
|
|
251
250
|
id: 'myComponent',
|
|
252
251
|
fieldset: {
|
|
@@ -257,7 +256,34 @@ describe('DeclarationField', () => {
|
|
|
257
256
|
items: [
|
|
258
257
|
{
|
|
259
258
|
value: 'true',
|
|
260
|
-
text: 'I understand and agree'
|
|
259
|
+
text: 'I understand and agree',
|
|
260
|
+
checked: false
|
|
261
|
+
}
|
|
262
|
+
]
|
|
263
|
+
})
|
|
264
|
+
)
|
|
265
|
+
})
|
|
266
|
+
|
|
267
|
+
it('sets Nunjucks component to false when not checked', () => {
|
|
268
|
+
def = {
|
|
269
|
+
...def,
|
|
270
|
+
hint: 'Please read and confirm the following'
|
|
271
|
+
} satisfies DeclarationFieldComponent
|
|
272
|
+
|
|
273
|
+
collection = new ComponentCollection([def], { model })
|
|
274
|
+
field = collection.fields[0]
|
|
275
|
+
const viewModel = field.getViewModel(getFormData('unchecked'))
|
|
276
|
+
|
|
277
|
+
expect(viewModel).toEqual(
|
|
278
|
+
expect.objectContaining({
|
|
279
|
+
hint: {
|
|
280
|
+
text: 'Please read and confirm the following'
|
|
281
|
+
},
|
|
282
|
+
items: [
|
|
283
|
+
{
|
|
284
|
+
value: 'true',
|
|
285
|
+
text: 'I understand and agree',
|
|
286
|
+
checked: false
|
|
261
287
|
}
|
|
262
288
|
]
|
|
263
289
|
})
|
|
@@ -272,14 +298,20 @@ describe('DeclarationField', () => {
|
|
|
272
298
|
|
|
273
299
|
collection = new ComponentCollection([def], { model })
|
|
274
300
|
field = collection.fields[0]
|
|
275
|
-
const viewModel = field.getViewModel(getFormData(
|
|
301
|
+
const viewModel = field.getViewModel(getFormData('true'))
|
|
276
302
|
|
|
277
303
|
expect(viewModel).toEqual(
|
|
278
304
|
expect.objectContaining({
|
|
279
|
-
values: ['true'],
|
|
280
305
|
hint: {
|
|
281
306
|
text: 'Please read and confirm the following'
|
|
282
|
-
}
|
|
307
|
+
},
|
|
308
|
+
items: [
|
|
309
|
+
{
|
|
310
|
+
value: 'true',
|
|
311
|
+
text: 'I understand and agree',
|
|
312
|
+
checked: true
|
|
313
|
+
}
|
|
314
|
+
]
|
|
283
315
|
})
|
|
284
316
|
)
|
|
285
317
|
})
|
|
@@ -301,14 +333,39 @@ describe('DeclarationField', () => {
|
|
|
301
333
|
collection = new ComponentCollection([def], { model })
|
|
302
334
|
field = collection.fields[0]
|
|
303
335
|
|
|
304
|
-
const viewModel = field.getViewModel(getFormData(
|
|
336
|
+
const viewModel = field.getViewModel(getFormData('true'))
|
|
337
|
+
|
|
338
|
+
expect(viewModel).toEqual(
|
|
339
|
+
expect.objectContaining({
|
|
340
|
+
items: [
|
|
341
|
+
{
|
|
342
|
+
value: 'true',
|
|
343
|
+
text: 'I consent to the processing of my personal data',
|
|
344
|
+
checked: true
|
|
345
|
+
}
|
|
346
|
+
]
|
|
347
|
+
})
|
|
348
|
+
)
|
|
349
|
+
})
|
|
350
|
+
|
|
351
|
+
it('sets checkbox as unchecked', () => {
|
|
352
|
+
def = {
|
|
353
|
+
...def,
|
|
354
|
+
hint: 'Please read and confirm the following'
|
|
355
|
+
} satisfies DeclarationFieldComponent
|
|
356
|
+
|
|
357
|
+
collection = new ComponentCollection([def], { model })
|
|
358
|
+
field = collection.fields[0]
|
|
359
|
+
|
|
360
|
+
const viewModel = field.getViewModel(getFormData(undefined))
|
|
305
361
|
|
|
306
362
|
expect(viewModel).toEqual(
|
|
307
363
|
expect.objectContaining({
|
|
308
364
|
items: [
|
|
309
365
|
{
|
|
310
366
|
value: 'true',
|
|
311
|
-
text: 'I
|
|
367
|
+
text: 'I understand and agree',
|
|
368
|
+
checked: false
|
|
312
369
|
}
|
|
313
370
|
]
|
|
314
371
|
})
|
|
@@ -68,12 +68,12 @@ export class DeclarationField extends FormComponent {
|
|
|
68
68
|
|
|
69
69
|
getFormValueFromState(state: FormSubmissionState) {
|
|
70
70
|
const { name } = this
|
|
71
|
-
return state[name] === true ? 'true' :
|
|
71
|
+
return state[name] === true ? 'true' : 'unchecked'
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
getFormDataFromState(state: FormSubmissionState): FormPayload {
|
|
75
75
|
const { name } = this
|
|
76
|
-
return { [name]: state[name] === true ? 'true' :
|
|
76
|
+
return { [name]: state[name] === true ? 'true' : 'unchecked' }
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
getStateFromValidForm(payload: FormPayload): FormState {
|
|
@@ -98,7 +98,7 @@ export class DeclarationField extends FormComponent {
|
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
getDisplayStringFromFormValue(value: FormValue | FormPayload): string {
|
|
101
|
-
return value ? this.declarationConfirmationLabel : ''
|
|
101
|
+
return value === 'true' ? this.declarationConfirmationLabel : 'Not provided'
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
getViewModel(payload: FormPayload, errors?: FormSubmissionError[]) {
|
|
@@ -110,6 +110,8 @@ export class DeclarationField extends FormComponent {
|
|
|
110
110
|
content,
|
|
111
111
|
declarationConfirmationLabel = defaultDeclarationConfirmationLabel
|
|
112
112
|
} = this
|
|
113
|
+
const isChecked =
|
|
114
|
+
payload[this.name] === 'true' || payload[this.name] === true
|
|
113
115
|
return {
|
|
114
116
|
...super.getViewModel(payload, errors),
|
|
115
117
|
hint: hint ? { text: hint } : undefined,
|
|
@@ -119,11 +121,11 @@ export class DeclarationField extends FormComponent {
|
|
|
119
121
|
}
|
|
120
122
|
},
|
|
121
123
|
content,
|
|
122
|
-
values: payload[this.name],
|
|
123
124
|
items: [
|
|
124
125
|
{
|
|
125
126
|
text: declarationConfirmationLabel,
|
|
126
|
-
value: 'true'
|
|
127
|
+
value: 'true',
|
|
128
|
+
checked: isChecked
|
|
127
129
|
}
|
|
128
130
|
]
|
|
129
131
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ComponentType,
|
|
3
|
+
type DeclarationFieldComponent,
|
|
3
4
|
type EastingNorthingFieldComponent,
|
|
4
5
|
type LatLongFieldComponent,
|
|
5
6
|
type NationalGridFieldNumberFieldComponent,
|
|
@@ -11,6 +12,7 @@ import {
|
|
|
11
12
|
getAnswerMarkdown
|
|
12
13
|
} from '~/src/server/plugins/engine/components/helpers/components.js'
|
|
13
14
|
import {
|
|
15
|
+
DeclarationField,
|
|
14
16
|
EastingNorthingField,
|
|
15
17
|
LatLongField,
|
|
16
18
|
NationalGridFieldNumberField,
|
|
@@ -216,6 +218,89 @@ describe('Location field formatting', () => {
|
|
|
216
218
|
})
|
|
217
219
|
})
|
|
218
220
|
|
|
221
|
+
describe('DeclarationField', () => {
|
|
222
|
+
let field: DeclarationField
|
|
223
|
+
|
|
224
|
+
beforeEach(() => {
|
|
225
|
+
const def: DeclarationFieldComponent = {
|
|
226
|
+
type: ComponentType.DeclarationField,
|
|
227
|
+
name: 'declField',
|
|
228
|
+
title: 'Declaration title',
|
|
229
|
+
content: '# markup heading',
|
|
230
|
+
options: {}
|
|
231
|
+
}
|
|
232
|
+
field = new DeclarationField(def, { model })
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
it('formats correctly when agreed', () => {
|
|
236
|
+
const state = {
|
|
237
|
+
declField: true
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const answer = getAnswer(field, state, { format: 'email' })
|
|
241
|
+
expect(answer).toBe('I understand and agree\n')
|
|
242
|
+
})
|
|
243
|
+
|
|
244
|
+
it('formats correctly when not agreed', () => {
|
|
245
|
+
const state = {
|
|
246
|
+
declField: false
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const answer = getAnswer(field, state, { format: 'email' })
|
|
250
|
+
expect(answer).toBe('Not provided\n')
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
it('formats for data output when agreed', () => {
|
|
254
|
+
const state = {
|
|
255
|
+
declField: true
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const answer = getAnswer(field, state, { format: 'data' })
|
|
259
|
+
expect(answer).toBe('true')
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
it('formats for data output when not agreed', () => {
|
|
263
|
+
const state = {
|
|
264
|
+
declField: false
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const answer = getAnswer(field, state, { format: 'data' })
|
|
268
|
+
expect(answer).toBe('false')
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
it('formats for data output when no value', () => {
|
|
272
|
+
const state = {}
|
|
273
|
+
|
|
274
|
+
const answer = getAnswer(field, state, { format: 'data' })
|
|
275
|
+
expect(answer).toBe('false')
|
|
276
|
+
})
|
|
277
|
+
|
|
278
|
+
it('formats for summary display when agreed', () => {
|
|
279
|
+
const state = {
|
|
280
|
+
declField: true
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const answer = getAnswer(field, state, { format: 'summary' })
|
|
284
|
+
expect(answer).toBe('I understand and agree')
|
|
285
|
+
})
|
|
286
|
+
|
|
287
|
+
it('formats for summary display when not agreed', () => {
|
|
288
|
+
const state = {
|
|
289
|
+
declField: false
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const answer = getAnswer(field, state, { format: 'summary' })
|
|
293
|
+
expect(answer).toBe('Not provided')
|
|
294
|
+
})
|
|
295
|
+
|
|
296
|
+
it('formats for summary display when no value', () => {
|
|
297
|
+
const state = {}
|
|
298
|
+
|
|
299
|
+
const answer = getAnswer(field, state, { format: 'summary' })
|
|
300
|
+
expect(answer).toBe('Not provided')
|
|
301
|
+
})
|
|
302
|
+
})
|
|
303
|
+
|
|
219
304
|
describe('getAnswerMarkdown', () => {
|
|
220
305
|
it('formats EastingNorthingField correctly', () => {
|
|
221
306
|
const def: EastingNorthingFieldComponent = {
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type FormDefinition,
|
|
3
|
+
type PageQuestion,
|
|
4
|
+
type RadiosFieldComponent
|
|
5
|
+
} from '@defra/forms-model'
|
|
6
|
+
|
|
1
7
|
import { FORM_PREFIX } from '~/src/server/constants.js'
|
|
2
8
|
import {
|
|
3
9
|
FormModel,
|
|
@@ -66,7 +72,7 @@ describe('SummaryViewModel', () => {
|
|
|
66
72
|
'Pizzas',
|
|
67
73
|
'Pizza'
|
|
68
74
|
],
|
|
69
|
-
values: ['Collection', 'Not
|
|
75
|
+
values: ['Collection', 'Not provided'],
|
|
70
76
|
answers: ['Collection', ''],
|
|
71
77
|
names: ['orderType', 'pizza']
|
|
72
78
|
},
|
|
@@ -281,6 +287,49 @@ describe('SummaryViewModel', () => {
|
|
|
281
287
|
})
|
|
282
288
|
}
|
|
283
289
|
)
|
|
290
|
+
|
|
291
|
+
it('should use correct summary labels', () => {
|
|
292
|
+
request.query.force = '' // Preview URL '?force'
|
|
293
|
+
const state = {
|
|
294
|
+
$$__referenceNumber: 'foobar',
|
|
295
|
+
orderType: 'collection',
|
|
296
|
+
pizza: []
|
|
297
|
+
} satisfies FormState
|
|
298
|
+
|
|
299
|
+
// Setup an optional question
|
|
300
|
+
const definitionOptional = structuredClone(definition) as FormDefinition
|
|
301
|
+
const firstPage = definitionOptional.pages[0] as PageQuestion
|
|
302
|
+
const firstComponent = firstPage.components[0] as RadiosFieldComponent
|
|
303
|
+
firstComponent.options.required = false
|
|
304
|
+
|
|
305
|
+
const model = new FormModel(definitionOptional, {
|
|
306
|
+
basePath: `${FORM_PREFIX}/test`
|
|
307
|
+
})
|
|
308
|
+
|
|
309
|
+
context = model.getFormContext(request, state)
|
|
310
|
+
|
|
311
|
+
const page = createPage(model, definition.pages[2])
|
|
312
|
+
|
|
313
|
+
summaryViewModel = new SummaryViewModel(request, page, context)
|
|
314
|
+
|
|
315
|
+
expect(summaryViewModel.details).toHaveLength(2)
|
|
316
|
+
|
|
317
|
+
const [details1, details2] = summaryViewModel.details
|
|
318
|
+
|
|
319
|
+
expect(details1.items[0]).toMatchObject({
|
|
320
|
+
name: 'orderType',
|
|
321
|
+
value: 'Collection',
|
|
322
|
+
title: 'How you would like to receive your pizza (optional)',
|
|
323
|
+
label: 'How would you like to receive your pizza?'
|
|
324
|
+
})
|
|
325
|
+
|
|
326
|
+
expect(details2.items[0]).toMatchObject({
|
|
327
|
+
name: 'pizza',
|
|
328
|
+
value: '',
|
|
329
|
+
title: 'Pizzas',
|
|
330
|
+
label: 'Pizza'
|
|
331
|
+
})
|
|
332
|
+
})
|
|
284
333
|
})
|
|
285
334
|
|
|
286
335
|
describe('SummaryPageController', () => {
|
|
@@ -342,7 +391,7 @@ describe('SummaryPageController', () => {
|
|
|
342
391
|
)
|
|
343
392
|
})
|
|
344
393
|
|
|
345
|
-
it('should display default page title for v2 form when title not
|
|
394
|
+
it('should display default page title for v2 form when title not provided', () => {
|
|
346
395
|
const state: FormState = {
|
|
347
396
|
$$__referenceNumber: 'foobar',
|
|
348
397
|
orderType: 'collection',
|
|
@@ -104,7 +104,7 @@ export class SummaryViewModel {
|
|
|
104
104
|
},
|
|
105
105
|
value: {
|
|
106
106
|
classes: 'app-prose-scope',
|
|
107
|
-
html: item.value || 'Not
|
|
107
|
+
html: item.value || 'Not provided'
|
|
108
108
|
},
|
|
109
109
|
actions: {
|
|
110
110
|
items
|
|
@@ -204,7 +204,7 @@ function ItemRepeat(
|
|
|
204
204
|
* Creates a form field detail item
|
|
205
205
|
* @see {@link DetailItemField}
|
|
206
206
|
*/
|
|
207
|
-
function ItemField(
|
|
207
|
+
export function ItemField(
|
|
208
208
|
page: PageControllerClass,
|
|
209
209
|
state: FormState,
|
|
210
210
|
field: Field,
|
|
@@ -216,7 +216,10 @@ function ItemField(
|
|
|
216
216
|
return {
|
|
217
217
|
name: field.name,
|
|
218
218
|
label: field.title,
|
|
219
|
-
title:
|
|
219
|
+
title:
|
|
220
|
+
field.options.required === false
|
|
221
|
+
? `${field.label} (optional)`
|
|
222
|
+
: field.label,
|
|
220
223
|
error: field.getFirstError(options.errors),
|
|
221
224
|
value: getAnswer(field, state),
|
|
222
225
|
href: getPageHref(page, options.path, {
|