@defra/forms-engine-plugin 4.0.24 → 4.0.26
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/forms/components.json +2 -2
- package/.server/server/plugins/engine/components/DeclarationField.d.ts +2 -0
- package/.server/server/plugins/engine/components/DeclarationField.js +8 -1
- package/.server/server/plugins/engine/components/DeclarationField.js.map +1 -1
- package/.server/server/plugins/engine/components/HiddenField.d.ts +21 -0
- package/.server/server/plugins/engine/components/HiddenField.js +50 -0
- package/.server/server/plugins/engine/components/HiddenField.js.map +1 -0
- package/.server/server/plugins/engine/components/Markdown.d.ts +2 -0
- package/.server/server/plugins/engine/components/Markdown.js +4 -1
- package/.server/server/plugins/engine/components/Markdown.js.map +1 -1
- package/.server/server/plugins/engine/components/helpers/components.d.ts +1 -1
- package/.server/server/plugins/engine/components/helpers/components.js +3 -0
- package/.server/server/plugins/engine/components/helpers/components.js.map +1 -1
- package/.server/server/plugins/engine/components/index.d.ts +1 -0
- package/.server/server/plugins/engine/components/index.js +1 -0
- package/.server/server/plugins/engine/components/index.js.map +1 -1
- package/.server/server/plugins/engine/helpers.js +2 -1
- package/.server/server/plugins/engine/helpers.js.map +1 -1
- package/.server/server/plugins/engine/index.js +4 -1
- package/.server/server/plugins/engine/index.js.map +1 -1
- package/.server/server/plugins/engine/models/FormModel.d.ts +2 -0
- package/.server/server/plugins/engine/models/FormModel.js +2 -0
- package/.server/server/plugins/engine/models/FormModel.js.map +1 -1
- package/.server/server/plugins/engine/pageControllers/PageController.d.ts +1 -1
- package/.server/server/plugins/engine/pageControllers/PageController.js +2 -8
- package/.server/server/plugins/engine/pageControllers/PageController.js.map +1 -1
- package/.server/server/plugins/engine/pageControllers/QuestionPageController.js +7 -0
- package/.server/server/plugins/engine/pageControllers/QuestionPageController.js.map +1 -1
- package/.server/server/plugins/engine/pageControllers/StatusPageController.js +8 -2
- package/.server/server/plugins/engine/pageControllers/StatusPageController.js.map +1 -1
- package/.server/server/plugins/engine/pageControllers/SummaryPageController.js +2 -1
- package/.server/server/plugins/engine/pageControllers/SummaryPageController.js.map +1 -1
- package/.server/server/plugins/engine/pageControllers/helpers/state.d.ts +11 -0
- package/.server/server/plugins/engine/pageControllers/helpers/state.js +66 -0
- package/.server/server/plugins/engine/pageControllers/helpers/state.js.map +1 -0
- package/.server/server/plugins/engine/routes/index.js +2 -1
- package/.server/server/plugins/engine/routes/index.js.map +1 -1
- package/.server/server/plugins/engine/services/formsService.d.ts +6 -0
- package/.server/server/plugins/engine/services/formsService.js +10 -0
- package/.server/server/plugins/engine/services/formsService.js.map +1 -1
- package/.server/server/plugins/engine/services/formsService.test.js +14 -0
- package/.server/server/plugins/engine/services/formsService.test.js.map +1 -0
- package/.server/server/plugins/engine/types.d.ts +4 -0
- package/.server/server/plugins/engine/types.js.map +1 -1
- package/.server/server/plugins/engine/views/components/declarationfield.html +1 -1
- package/.server/server/plugins/engine/views/components/hiddenfield.html +3 -0
- package/.server/server/plugins/engine/views/components/markdown.html +1 -1
- package/.server/server/plugins/engine/views/confirmation.html +6 -1
- package/.server/server/plugins/engine/views/partials/form.html +9 -1
- package/.server/server/plugins/nunjucks/filters/field.d.ts +1 -1
- package/.server/server/services/cacheService.d.ts +3 -7
- package/.server/server/services/cacheService.js.map +1 -1
- package/.server/server/types.d.ts +1 -0
- package/.server/server/types.js.map +1 -1
- package/.server/server/utils/file-form-service.d.ts +6 -0
- package/.server/server/utils/file-form-service.js +22 -1
- package/.server/server/utils/file-form-service.js.map +1 -1
- package/.server/server/utils/file-form-service.test.js +86 -0
- package/.server/server/utils/file-form-service.test.js.map +1 -0
- package/package.json +3 -2
- package/src/server/forms/components.json +2 -2
- package/src/server/plugins/engine/components/DeclarationField.test.ts +24 -0
- package/src/server/plugins/engine/components/DeclarationField.ts +20 -2
- package/src/server/plugins/engine/components/HiddenField.test.ts +188 -0
- package/src/server/plugins/engine/components/HiddenField.ts +68 -0
- package/src/server/plugins/engine/components/Markdown.ts +4 -1
- package/src/server/plugins/engine/components/helpers/components.ts +5 -0
- package/src/server/plugins/engine/components/helpers/helpers.test.ts +17 -0
- package/src/server/plugins/engine/components/index.ts +1 -0
- package/src/server/plugins/engine/helpers.ts +2 -1
- package/src/server/plugins/engine/index.ts +5 -2
- package/src/server/plugins/engine/models/FormModel.ts +3 -0
- package/src/server/plugins/engine/pageControllers/PageController.test.ts +4 -11
- package/src/server/plugins/engine/pageControllers/PageController.ts +1 -9
- package/src/server/plugins/engine/pageControllers/QuestionPageController.ts +7 -0
- package/src/server/plugins/engine/pageControllers/StatusPageController.ts +9 -2
- package/src/server/plugins/engine/pageControllers/SummaryPageController.ts +5 -1
- package/src/server/plugins/engine/pageControllers/helpers/state.test.ts +221 -0
- package/src/server/plugins/engine/pageControllers/helpers/state.ts +93 -0
- package/src/server/plugins/engine/routes/index.test.ts +24 -11
- package/src/server/plugins/engine/routes/index.ts +1 -1
- package/src/server/plugins/engine/services/formsService.js +10 -0
- package/src/server/plugins/engine/services/formsService.test.js +21 -0
- package/src/server/plugins/engine/types.ts +5 -0
- package/src/server/plugins/engine/views/components/declarationfield.html +1 -1
- package/src/server/plugins/engine/views/components/hiddenfield.html +3 -0
- package/src/server/plugins/engine/views/components/markdown.html +1 -1
- package/src/server/plugins/engine/views/confirmation.html +6 -1
- package/src/server/plugins/engine/views/partials/form.html +9 -1
- package/src/server/services/cacheService.ts +3 -2
- package/src/server/types.ts +1 -0
- package/src/server/utils/file-form-service.js +27 -1
- package/src/server/utils/file-form-service.test.js +114 -0
|
@@ -33,6 +33,12 @@ export class FileFormService {
|
|
|
33
33
|
* @returns {FormMetadata}
|
|
34
34
|
*/
|
|
35
35
|
getFormMetadata(slug: string): FormMetadata;
|
|
36
|
+
/**
|
|
37
|
+
* Get the form metadata by form id
|
|
38
|
+
* @param {string} id - the form id
|
|
39
|
+
* @returns {FormMetadata}
|
|
40
|
+
*/
|
|
41
|
+
getFormMetadataById(id: string): FormMetadata;
|
|
36
42
|
/**
|
|
37
43
|
* Get the form defintion by id
|
|
38
44
|
* @param {string} id - the form id
|
|
@@ -89,6 +89,19 @@ export class FileFormService {
|
|
|
89
89
|
return metadata;
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
+
/**
|
|
93
|
+
* Get the form metadata by form id
|
|
94
|
+
* @param {string} id - the form id
|
|
95
|
+
* @returns {FormMetadata}
|
|
96
|
+
*/
|
|
97
|
+
getFormMetadataById(id) {
|
|
98
|
+
const metadata = Array.from(this.#metadata.values()).find(form => form.id === id);
|
|
99
|
+
if (!metadata) {
|
|
100
|
+
throw new Error(`Form metadata id '${id}' not found`);
|
|
101
|
+
}
|
|
102
|
+
return metadata;
|
|
103
|
+
}
|
|
104
|
+
|
|
92
105
|
/**
|
|
93
106
|
* Get the form defintion by id
|
|
94
107
|
* @param {string} id - the form id
|
|
@@ -116,6 +129,14 @@ export class FileFormService {
|
|
|
116
129
|
getFormMetadata: slug => {
|
|
117
130
|
return Promise.resolve(this.getFormMetadata(slug));
|
|
118
131
|
},
|
|
132
|
+
/**
|
|
133
|
+
* Get the form metadata by form id
|
|
134
|
+
* @param {string} id
|
|
135
|
+
* @returns {Promise<FormMetadata>}
|
|
136
|
+
*/
|
|
137
|
+
getFormMetadataById: id => {
|
|
138
|
+
return Promise.resolve(this.getFormMetadataById(id));
|
|
139
|
+
},
|
|
119
140
|
/**
|
|
120
141
|
* Get the form defintion by id
|
|
121
142
|
* @param {string} id
|
|
@@ -129,6 +150,6 @@ export class FileFormService {
|
|
|
129
150
|
}
|
|
130
151
|
|
|
131
152
|
/**
|
|
132
|
-
* @import { FormMetadata, FormDefinition } from '@defra/forms-model'
|
|
153
|
+
* @import { FormMetadata, FormDefinition, FormStatus } from '@defra/forms-model'
|
|
133
154
|
*/
|
|
134
155
|
//# sourceMappingURL=file-form-service.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-form-service.js","names":["fs","path","YAML","FileFormService","metadata","Map","definition","addForm","filepath","readForm","set","slug","id","ext","extname","toLowerCase","readJsonForm","readYamlForm","Error","JSON","parse","readFile","getFormMetadata","get","getFormDefinition","toFormsService","Promise","resolve"],"sources":["../../../src/server/utils/file-form-service.js"],"sourcesContent":["import fs from 'fs/promises'\nimport path from 'node:path'\n\nimport YAML from 'yaml'\n\n/**\n * FileFormService class\n */\nexport class FileFormService {\n /**\n * The map of form metadatas by slug\n * @type {Map<string, FormMetadata>}\n */\n #metadata = new Map()\n\n /**\n * The map of form definitions by id\n * @type {Map<string, FormDefinition>}\n */\n #definition = new Map()\n\n /**\n * Add form from a file\n * @param {string} filepath - the file path\n * @param {FormMetadata} metadata - the metadata to use for this form\n * @returns {Promise<FormDefinition>}\n */\n async addForm(filepath, metadata) {\n const definition = await this.readForm(filepath)\n\n this.#metadata.set(metadata.slug, metadata)\n this.#definition.set(metadata.id, definition)\n\n return definition\n }\n\n /**\n * Read the form definition from file\n * @param {string} filepath - the file path\n * @returns {Promise<FormDefinition>}\n */\n async readForm(filepath) {\n const ext = path.extname(filepath).toLowerCase()\n\n switch (ext) {\n case '.json':\n return this.readJsonForm(filepath)\n case '.yaml':\n return this.readYamlForm(filepath)\n default:\n throw new Error(`Invalid file extension '${ext}'`)\n }\n }\n\n /**\n * Read the form definition from a json file\n * @param {string} filepath - the file path\n * @returns {Promise<FormDefinition>}\n */\n async readJsonForm(filepath) {\n /**\n * @type {FormDefinition}\n */\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n const definition = JSON.parse(await fs.readFile(filepath, 'utf8'))\n\n return definition\n }\n\n /**\n * Read the form definition from a yaml file\n * @param {string} filepath - the file path\n * @returns {Promise<FormDefinition>}\n */\n async readYamlForm(filepath) {\n /**\n * @type {FormDefinition}\n */\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n const definition = YAML.parse(await fs.readFile(filepath, 'utf8'))\n\n return definition\n }\n\n /**\n * Get the form metadata by slug\n * @param {string} slug - the form slug\n * @returns {FormMetadata}\n */\n getFormMetadata(slug) {\n const metadata = this.#metadata.get(slug)\n\n if (!metadata) {\n throw new Error(`Form metadata '${slug}' not found`)\n }\n\n return metadata\n }\n\n /**\n * Get the form defintion by id\n * @param {string} id - the form id\n * @returns {FormDefinition}\n */\n getFormDefinition(id) {\n const definition = this.#definition.get(id)\n\n if (!definition) {\n throw new Error(`Form definition '${id}' not found`)\n }\n\n return definition\n }\n\n /**\n * Returns a FormsService compliant interface\n * @returns {import('~/src/server/types.js').FormsService}\n */\n toFormsService() {\n return {\n /**\n * Get the form metadata by slug\n * @param {string} slug\n * @returns {Promise<FormMetadata>}\n */\n getFormMetadata: (slug) => {\n return Promise.resolve(this.getFormMetadata(slug))\n },\n\n /**\n * Get the form defintion by id\n * @param {string} id\n * @returns {Promise<FormDefinition>}\n */\n getFormDefinition: (id) => {\n return Promise.resolve(this.getFormDefinition(id))\n }\n }\n }\n}\n\n/**\n * @import { FormMetadata, FormDefinition } from '@defra/forms-model'\n */\n"],"mappings":"AAAA,OAAOA,EAAE,MAAM,aAAa;AAC5B,OAAOC,IAAI,MAAM,WAAW;AAE5B,OAAOC,IAAI,MAAM,MAAM;;AAEvB;AACA;AACA;AACA,OAAO,MAAMC,eAAe,CAAC;EAC3B;AACF;AACA;AACA;EACE,CAACC,QAAQ,GAAG,IAAIC,GAAG,CAAC,CAAC;;EAErB;AACF;AACA;AACA;EACE,CAACC,UAAU,GAAG,IAAID,GAAG,CAAC,CAAC;;EAEvB;AACF;AACA;AACA;AACA;AACA;EACE,MAAME,OAAOA,CAACC,QAAQ,EAAEJ,QAAQ,EAAE;IAChC,MAAME,UAAU,GAAG,MAAM,IAAI,CAACG,QAAQ,CAACD,QAAQ,CAAC;IAEhD,IAAI,CAAC,CAACJ,QAAQ,CAACM,GAAG,CAACN,QAAQ,CAACO,IAAI,EAAEP,QAAQ,CAAC;IAC3C,IAAI,CAAC,CAACE,UAAU,CAACI,GAAG,CAACN,QAAQ,CAACQ,EAAE,EAAEN,UAAU,CAAC;IAE7C,OAAOA,UAAU;EACnB;;EAEA;AACF;AACA;AACA;AACA;EACE,MAAMG,QAAQA,CAACD,QAAQ,EAAE;IACvB,MAAMK,GAAG,GAAGZ,IAAI,CAACa,OAAO,CAACN,QAAQ,CAAC,CAACO,WAAW,CAAC,CAAC;IAEhD,QAAQF,GAAG;MACT,KAAK,OAAO;QACV,OAAO,IAAI,CAACG,YAAY,CAACR,QAAQ,CAAC;MACpC,KAAK,OAAO;QACV,OAAO,IAAI,CAACS,YAAY,CAACT,QAAQ,CAAC;MACpC;QACE,MAAM,IAAIU,KAAK,CAAC,2BAA2BL,GAAG,GAAG,CAAC;IACtD;EACF;;EAEA;AACF;AACA;AACA;AACA;EACE,MAAMG,YAAYA,CAACR,QAAQ,EAAE;IAC3B;AACJ;AACA;IACI;IACA,MAAMF,UAAU,GAAGa,IAAI,CAACC,KAAK,CAAC,MAAMpB,EAAE,CAACqB,QAAQ,CAACb,QAAQ,EAAE,MAAM,CAAC,CAAC;IAElE,OAAOF,UAAU;EACnB;;EAEA;AACF;AACA;AACA;AACA;EACE,MAAMW,YAAYA,CAACT,QAAQ,EAAE;IAC3B;AACJ;AACA;IACI;IACA,MAAMF,UAAU,GAAGJ,IAAI,CAACkB,KAAK,CAAC,MAAMpB,EAAE,CAACqB,QAAQ,CAACb,QAAQ,EAAE,MAAM,CAAC,CAAC;IAElE,OAAOF,UAAU;EACnB;;EAEA;AACF;AACA;AACA;AACA;EACEgB,eAAeA,CAACX,IAAI,EAAE;IACpB,MAAMP,QAAQ,GAAG,IAAI,CAAC,CAACA,QAAQ,CAACmB,GAAG,CAACZ,IAAI,CAAC;IAEzC,IAAI,CAACP,QAAQ,EAAE;MACb,MAAM,IAAIc,KAAK,CAAC,kBAAkBP,IAAI,aAAa,CAAC;IACtD;IAEA,OAAOP,QAAQ;EACjB;;EAEA;AACF;AACA;AACA;AACA;EACEoB,
|
|
1
|
+
{"version":3,"file":"file-form-service.js","names":["fs","path","YAML","FileFormService","metadata","Map","definition","addForm","filepath","readForm","set","slug","id","ext","extname","toLowerCase","readJsonForm","readYamlForm","Error","JSON","parse","readFile","getFormMetadata","get","getFormMetadataById","Array","from","values","find","form","getFormDefinition","toFormsService","Promise","resolve"],"sources":["../../../src/server/utils/file-form-service.js"],"sourcesContent":["import fs from 'fs/promises'\nimport path from 'node:path'\n\nimport YAML from 'yaml'\n\n/**\n * FileFormService class\n */\nexport class FileFormService {\n /**\n * The map of form metadatas by slug\n * @type {Map<string, FormMetadata>}\n */\n #metadata = new Map()\n\n /**\n * The map of form definitions by id\n * @type {Map<string, FormDefinition>}\n */\n #definition = new Map()\n\n /**\n * Add form from a file\n * @param {string} filepath - the file path\n * @param {FormMetadata} metadata - the metadata to use for this form\n * @returns {Promise<FormDefinition>}\n */\n async addForm(filepath, metadata) {\n const definition = await this.readForm(filepath)\n\n this.#metadata.set(metadata.slug, metadata)\n this.#definition.set(metadata.id, definition)\n\n return definition\n }\n\n /**\n * Read the form definition from file\n * @param {string} filepath - the file path\n * @returns {Promise<FormDefinition>}\n */\n async readForm(filepath) {\n const ext = path.extname(filepath).toLowerCase()\n\n switch (ext) {\n case '.json':\n return this.readJsonForm(filepath)\n case '.yaml':\n return this.readYamlForm(filepath)\n default:\n throw new Error(`Invalid file extension '${ext}'`)\n }\n }\n\n /**\n * Read the form definition from a json file\n * @param {string} filepath - the file path\n * @returns {Promise<FormDefinition>}\n */\n async readJsonForm(filepath) {\n /**\n * @type {FormDefinition}\n */\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n const definition = JSON.parse(await fs.readFile(filepath, 'utf8'))\n\n return definition\n }\n\n /**\n * Read the form definition from a yaml file\n * @param {string} filepath - the file path\n * @returns {Promise<FormDefinition>}\n */\n async readYamlForm(filepath) {\n /**\n * @type {FormDefinition}\n */\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n const definition = YAML.parse(await fs.readFile(filepath, 'utf8'))\n\n return definition\n }\n\n /**\n * Get the form metadata by slug\n * @param {string} slug - the form slug\n * @returns {FormMetadata}\n */\n getFormMetadata(slug) {\n const metadata = this.#metadata.get(slug)\n\n if (!metadata) {\n throw new Error(`Form metadata '${slug}' not found`)\n }\n\n return metadata\n }\n\n /**\n * Get the form metadata by form id\n * @param {string} id - the form id\n * @returns {FormMetadata}\n */\n getFormMetadataById(id) {\n const metadata = Array.from(this.#metadata.values()).find(\n (form) => form.id === id\n )\n\n if (!metadata) {\n throw new Error(`Form metadata id '${id}' not found`)\n }\n\n return metadata\n }\n\n /**\n * Get the form defintion by id\n * @param {string} id - the form id\n * @returns {FormDefinition}\n */\n getFormDefinition(id) {\n const definition = this.#definition.get(id)\n\n if (!definition) {\n throw new Error(`Form definition '${id}' not found`)\n }\n\n return definition\n }\n\n /**\n * Returns a FormsService compliant interface\n * @returns {import('~/src/server/types.js').FormsService}\n */\n toFormsService() {\n return {\n /**\n * Get the form metadata by slug\n * @param {string} slug\n * @returns {Promise<FormMetadata>}\n */\n getFormMetadata: (slug) => {\n return Promise.resolve(this.getFormMetadata(slug))\n },\n\n /**\n * Get the form metadata by form id\n * @param {string} id\n * @returns {Promise<FormMetadata>}\n */\n getFormMetadataById: (id) => {\n return Promise.resolve(this.getFormMetadataById(id))\n },\n\n /**\n * Get the form defintion by id\n * @param {string} id\n * @returns {Promise<FormDefinition>}\n */\n getFormDefinition: (id) => {\n return Promise.resolve(this.getFormDefinition(id))\n }\n }\n }\n}\n\n/**\n * @import { FormMetadata, FormDefinition, FormStatus } from '@defra/forms-model'\n */\n"],"mappings":"AAAA,OAAOA,EAAE,MAAM,aAAa;AAC5B,OAAOC,IAAI,MAAM,WAAW;AAE5B,OAAOC,IAAI,MAAM,MAAM;;AAEvB;AACA;AACA;AACA,OAAO,MAAMC,eAAe,CAAC;EAC3B;AACF;AACA;AACA;EACE,CAACC,QAAQ,GAAG,IAAIC,GAAG,CAAC,CAAC;;EAErB;AACF;AACA;AACA;EACE,CAACC,UAAU,GAAG,IAAID,GAAG,CAAC,CAAC;;EAEvB;AACF;AACA;AACA;AACA;AACA;EACE,MAAME,OAAOA,CAACC,QAAQ,EAAEJ,QAAQ,EAAE;IAChC,MAAME,UAAU,GAAG,MAAM,IAAI,CAACG,QAAQ,CAACD,QAAQ,CAAC;IAEhD,IAAI,CAAC,CAACJ,QAAQ,CAACM,GAAG,CAACN,QAAQ,CAACO,IAAI,EAAEP,QAAQ,CAAC;IAC3C,IAAI,CAAC,CAACE,UAAU,CAACI,GAAG,CAACN,QAAQ,CAACQ,EAAE,EAAEN,UAAU,CAAC;IAE7C,OAAOA,UAAU;EACnB;;EAEA;AACF;AACA;AACA;AACA;EACE,MAAMG,QAAQA,CAACD,QAAQ,EAAE;IACvB,MAAMK,GAAG,GAAGZ,IAAI,CAACa,OAAO,CAACN,QAAQ,CAAC,CAACO,WAAW,CAAC,CAAC;IAEhD,QAAQF,GAAG;MACT,KAAK,OAAO;QACV,OAAO,IAAI,CAACG,YAAY,CAACR,QAAQ,CAAC;MACpC,KAAK,OAAO;QACV,OAAO,IAAI,CAACS,YAAY,CAACT,QAAQ,CAAC;MACpC;QACE,MAAM,IAAIU,KAAK,CAAC,2BAA2BL,GAAG,GAAG,CAAC;IACtD;EACF;;EAEA;AACF;AACA;AACA;AACA;EACE,MAAMG,YAAYA,CAACR,QAAQ,EAAE;IAC3B;AACJ;AACA;IACI;IACA,MAAMF,UAAU,GAAGa,IAAI,CAACC,KAAK,CAAC,MAAMpB,EAAE,CAACqB,QAAQ,CAACb,QAAQ,EAAE,MAAM,CAAC,CAAC;IAElE,OAAOF,UAAU;EACnB;;EAEA;AACF;AACA;AACA;AACA;EACE,MAAMW,YAAYA,CAACT,QAAQ,EAAE;IAC3B;AACJ;AACA;IACI;IACA,MAAMF,UAAU,GAAGJ,IAAI,CAACkB,KAAK,CAAC,MAAMpB,EAAE,CAACqB,QAAQ,CAACb,QAAQ,EAAE,MAAM,CAAC,CAAC;IAElE,OAAOF,UAAU;EACnB;;EAEA;AACF;AACA;AACA;AACA;EACEgB,eAAeA,CAACX,IAAI,EAAE;IACpB,MAAMP,QAAQ,GAAG,IAAI,CAAC,CAACA,QAAQ,CAACmB,GAAG,CAACZ,IAAI,CAAC;IAEzC,IAAI,CAACP,QAAQ,EAAE;MACb,MAAM,IAAIc,KAAK,CAAC,kBAAkBP,IAAI,aAAa,CAAC;IACtD;IAEA,OAAOP,QAAQ;EACjB;;EAEA;AACF;AACA;AACA;AACA;EACEoB,mBAAmBA,CAACZ,EAAE,EAAE;IACtB,MAAMR,QAAQ,GAAGqB,KAAK,CAACC,IAAI,CAAC,IAAI,CAAC,CAACtB,QAAQ,CAACuB,MAAM,CAAC,CAAC,CAAC,CAACC,IAAI,CACtDC,IAAI,IAAKA,IAAI,CAACjB,EAAE,KAAKA,EACxB,CAAC;IAED,IAAI,CAACR,QAAQ,EAAE;MACb,MAAM,IAAIc,KAAK,CAAC,qBAAqBN,EAAE,aAAa,CAAC;IACvD;IAEA,OAAOR,QAAQ;EACjB;;EAEA;AACF;AACA;AACA;AACA;EACE0B,iBAAiBA,CAAClB,EAAE,EAAE;IACpB,MAAMN,UAAU,GAAG,IAAI,CAAC,CAACA,UAAU,CAACiB,GAAG,CAACX,EAAE,CAAC;IAE3C,IAAI,CAACN,UAAU,EAAE;MACf,MAAM,IAAIY,KAAK,CAAC,oBAAoBN,EAAE,aAAa,CAAC;IACtD;IAEA,OAAON,UAAU;EACnB;;EAEA;AACF;AACA;AACA;EACEyB,cAAcA,CAAA,EAAG;IACf,OAAO;MACL;AACN;AACA;AACA;AACA;MACMT,eAAe,EAAGX,IAAI,IAAK;QACzB,OAAOqB,OAAO,CAACC,OAAO,CAAC,IAAI,CAACX,eAAe,CAACX,IAAI,CAAC,CAAC;MACpD,CAAC;MAED;AACN;AACA;AACA;AACA;MACMa,mBAAmB,EAAGZ,EAAE,IAAK;QAC3B,OAAOoB,OAAO,CAACC,OAAO,CAAC,IAAI,CAACT,mBAAmB,CAACZ,EAAE,CAAC,CAAC;MACtD,CAAC;MAED;AACN;AACA;AACA;AACA;MACMkB,iBAAiB,EAAGlB,EAAE,IAAK;QACzB,OAAOoB,OAAO,CAACC,OAAO,CAAC,IAAI,CAACH,iBAAiB,CAAClB,EAAE,CAAC,CAAC;MACpD;IACF,CAAC;EACH;AACF;;AAEA;AACA;AACA","ignoreList":[]}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { FormStatus } from "../routes/types.js";
|
|
3
|
+
import { FileFormService } from "./file-form-service.js";
|
|
4
|
+
describe('File-form-service', () => {
|
|
5
|
+
/** @type {FileFormService} */
|
|
6
|
+
let service;
|
|
7
|
+
beforeEach(async () => {
|
|
8
|
+
const now = new Date();
|
|
9
|
+
const user = {
|
|
10
|
+
id: 'user',
|
|
11
|
+
displayName: 'Username'
|
|
12
|
+
};
|
|
13
|
+
const author = {
|
|
14
|
+
createdAt: now,
|
|
15
|
+
createdBy: user,
|
|
16
|
+
updatedAt: now,
|
|
17
|
+
updatedBy: user
|
|
18
|
+
};
|
|
19
|
+
service = new FileFormService();
|
|
20
|
+
const metadata = {
|
|
21
|
+
organisation: 'Defra',
|
|
22
|
+
teamName: 'Team name',
|
|
23
|
+
teamEmail: 'team@defra.gov.uk',
|
|
24
|
+
submissionGuidance: "Thanks for your submission, we'll be in touch",
|
|
25
|
+
notificationEmail: 'email@domain.com',
|
|
26
|
+
...author,
|
|
27
|
+
live: author
|
|
28
|
+
};
|
|
29
|
+
await service.addForm(`${join(import.meta.dirname, '../../../test/form/definitions')}/components.json`, {
|
|
30
|
+
...metadata,
|
|
31
|
+
id: '95e92559-968d-44ae-8666-2b1ad3dffd31',
|
|
32
|
+
title: 'Form test',
|
|
33
|
+
slug: 'form-test'
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
describe('metadata by slug', () => {
|
|
37
|
+
it('should get form metadata by slug', () => {
|
|
38
|
+
const meta = service.getFormMetadata('form-test');
|
|
39
|
+
expect(meta.id).toBe('95e92559-968d-44ae-8666-2b1ad3dffd31');
|
|
40
|
+
expect(meta.title).toBe('Form test');
|
|
41
|
+
});
|
|
42
|
+
it('should throw if not found', () => {
|
|
43
|
+
expect(() => service.getFormMetadata('form-test-missing')).toThrow("Form metadata 'form-test-missing' not found");
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
describe('metadata by id', () => {
|
|
47
|
+
it('should get form metadata by id', () => {
|
|
48
|
+
const meta = service.getFormMetadataById('95e92559-968d-44ae-8666-2b1ad3dffd31');
|
|
49
|
+
expect(meta.id).toBe('95e92559-968d-44ae-8666-2b1ad3dffd31');
|
|
50
|
+
expect(meta.title).toBe('Form test');
|
|
51
|
+
});
|
|
52
|
+
it('should throw if not found', () => {
|
|
53
|
+
expect(() => service.getFormMetadataById('id-missing')).toThrow("Form metadata id 'id-missing' not found");
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
describe('definition by id', () => {
|
|
57
|
+
it('should get form definition by id', () => {
|
|
58
|
+
const form = service.getFormDefinition('95e92559-968d-44ae-8666-2b1ad3dffd31');
|
|
59
|
+
expect(form.name).toBe('All components');
|
|
60
|
+
expect(form.startPage).toBe('/all-components');
|
|
61
|
+
});
|
|
62
|
+
it('should throw if not found', () => {
|
|
63
|
+
expect(() => service.getFormDefinition('id-missing')).toThrow("Form definition 'id-missing' not found");
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
describe('toFormsService', () => {
|
|
67
|
+
it('should create interface', async () => {
|
|
68
|
+
const interfaceImpl = service.toFormsService();
|
|
69
|
+
const res1 = await interfaceImpl.getFormMetadata('form-test');
|
|
70
|
+
expect(res1.id).toBe('95e92559-968d-44ae-8666-2b1ad3dffd31');
|
|
71
|
+
expect(res1.title).toBe('Form test');
|
|
72
|
+
const res2 = await interfaceImpl.getFormMetadataById('95e92559-968d-44ae-8666-2b1ad3dffd31');
|
|
73
|
+
expect(res2.id).toBe('95e92559-968d-44ae-8666-2b1ad3dffd31');
|
|
74
|
+
expect(res2.title).toBe('Form test');
|
|
75
|
+
const res3 = await interfaceImpl.getFormDefinition('95e92559-968d-44ae-8666-2b1ad3dffd31', FormStatus.Draft);
|
|
76
|
+
expect(res3?.name).toBe('All components');
|
|
77
|
+
expect(res3?.startPage).toBe('/all-components');
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
describe('readForm', () => {
|
|
81
|
+
it('should throw if invalid extension', async () => {
|
|
82
|
+
await expect(service.readForm('/some-folder/some-file.bad')).rejects.toThrow("Invalid file extension '.bad'");
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
//# sourceMappingURL=file-form-service.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-form-service.test.js","names":["join","FormStatus","FileFormService","describe","service","beforeEach","now","Date","user","id","displayName","author","createdAt","createdBy","updatedAt","updatedBy","metadata","organisation","teamName","teamEmail","submissionGuidance","notificationEmail","live","addForm","import","meta","dirname","title","slug","it","getFormMetadata","expect","toBe","toThrow","getFormMetadataById","form","getFormDefinition","name","startPage","interfaceImpl","toFormsService","res1","res2","res3","Draft","readForm","rejects"],"sources":["../../../src/server/utils/file-form-service.test.js"],"sourcesContent":["import { join } from 'node:path'\n\nimport { FormStatus } from '~/src/server/routes/types.js'\nimport { FileFormService } from '~/src/server/utils/file-form-service.js'\n\ndescribe('File-form-service', () => {\n /** @type {FileFormService} */\n let service\n beforeEach(async () => {\n const now = new Date()\n const user = { id: 'user', displayName: 'Username' }\n const author = {\n createdAt: now,\n createdBy: user,\n updatedAt: now,\n updatedBy: user\n }\n service = new FileFormService()\n const metadata = {\n organisation: 'Defra',\n teamName: 'Team name',\n teamEmail: 'team@defra.gov.uk',\n submissionGuidance: \"Thanks for your submission, we'll be in touch\",\n notificationEmail: 'email@domain.com',\n ...author,\n live: author\n }\n await service.addForm(\n `${join(import.meta.dirname, '../../../test/form/definitions')}/components.json`,\n {\n ...metadata,\n id: '95e92559-968d-44ae-8666-2b1ad3dffd31',\n title: 'Form test',\n slug: 'form-test'\n }\n )\n })\n\n describe('metadata by slug', () => {\n it('should get form metadata by slug', () => {\n const meta = service.getFormMetadata('form-test')\n expect(meta.id).toBe('95e92559-968d-44ae-8666-2b1ad3dffd31')\n expect(meta.title).toBe('Form test')\n })\n\n it('should throw if not found', () => {\n expect(() => service.getFormMetadata('form-test-missing')).toThrow(\n \"Form metadata 'form-test-missing' not found\"\n )\n })\n })\n\n describe('metadata by id', () => {\n it('should get form metadata by id', () => {\n const meta = service.getFormMetadataById(\n '95e92559-968d-44ae-8666-2b1ad3dffd31'\n )\n expect(meta.id).toBe('95e92559-968d-44ae-8666-2b1ad3dffd31')\n expect(meta.title).toBe('Form test')\n })\n\n it('should throw if not found', () => {\n expect(() => service.getFormMetadataById('id-missing')).toThrow(\n \"Form metadata id 'id-missing' not found\"\n )\n })\n })\n\n describe('definition by id', () => {\n it('should get form definition by id', () => {\n const form = service.getFormDefinition(\n '95e92559-968d-44ae-8666-2b1ad3dffd31'\n )\n expect(form.name).toBe('All components')\n expect(form.startPage).toBe('/all-components')\n })\n\n it('should throw if not found', () => {\n expect(() => service.getFormDefinition('id-missing')).toThrow(\n \"Form definition 'id-missing' not found\"\n )\n })\n })\n\n describe('toFormsService', () => {\n it('should create interface', async () => {\n const interfaceImpl = service.toFormsService()\n const res1 = await interfaceImpl.getFormMetadata('form-test')\n expect(res1.id).toBe('95e92559-968d-44ae-8666-2b1ad3dffd31')\n expect(res1.title).toBe('Form test')\n\n const res2 = await interfaceImpl.getFormMetadataById(\n '95e92559-968d-44ae-8666-2b1ad3dffd31'\n )\n expect(res2.id).toBe('95e92559-968d-44ae-8666-2b1ad3dffd31')\n expect(res2.title).toBe('Form test')\n\n const res3 = await interfaceImpl.getFormDefinition(\n '95e92559-968d-44ae-8666-2b1ad3dffd31',\n FormStatus.Draft\n )\n expect(res3?.name).toBe('All components')\n expect(res3?.startPage).toBe('/all-components')\n })\n })\n\n describe('readForm', () => {\n it('should throw if invalid extension', async () => {\n await expect(\n service.readForm('/some-folder/some-file.bad')\n ).rejects.toThrow(\"Invalid file extension '.bad'\")\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.26",
|
|
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.585",
|
|
74
74
|
"@defra/hapi-tracing": "^1.29.0",
|
|
75
75
|
"@elastic/ecs-pino-format": "^1.5.0",
|
|
76
76
|
"@hapi/boom": "^10.0.1",
|
|
@@ -91,6 +91,7 @@
|
|
|
91
91
|
"blankie": "^5.0.0",
|
|
92
92
|
"blipp": "^4.0.2",
|
|
93
93
|
"btoa": "^1.2.1",
|
|
94
|
+
"chokidar": "3.6.0",
|
|
94
95
|
"convict": "^6.2.4",
|
|
95
96
|
"date-fns": "^4.1.0",
|
|
96
97
|
"dotenv": "^17.2.3",
|
|
@@ -139,7 +139,7 @@
|
|
|
139
139
|
"type": "Markdown",
|
|
140
140
|
"name": "markdown",
|
|
141
141
|
"title": "Title",
|
|
142
|
-
"content": "### This is
|
|
142
|
+
"content": "# Markdown - This is H1\n## This is H2\n### This is H3\n#### This is H4\n##### This is H5\n###### This is H6\n\n[An internal link](http://localhost:3009/fictional-page)\n\n[An external link](https://defra.gov.uk/fictional-page)",
|
|
143
143
|
"options": {},
|
|
144
144
|
"schema": {}
|
|
145
145
|
},
|
|
@@ -147,7 +147,7 @@
|
|
|
147
147
|
"type": "DeclarationField",
|
|
148
148
|
"name": "declaration",
|
|
149
149
|
"title": "Declaration",
|
|
150
|
-
"content": "
|
|
150
|
+
"content": "# H1\nBy submitting this form, I agree to:\n\n- Provide accurate and complete information\n- Comply with all applicable regulations\n- Accept responsibility for any false statements",
|
|
151
151
|
"hint": "Please read and confirm the following terms",
|
|
152
152
|
"options": {
|
|
153
153
|
"required": false
|
|
@@ -11,6 +11,8 @@ import {
|
|
|
11
11
|
} from '~/src/server/plugins/engine/components/helpers/components.js'
|
|
12
12
|
import { FormModel } from '~/src/server/plugins/engine/models/FormModel.js'
|
|
13
13
|
import definition from '~/test/form/definitions/blank.js'
|
|
14
|
+
import declarationWithGuidance from '~/test/form/definitions/declaration-with-guidance.js'
|
|
15
|
+
import declarationWithoutGuidance from '~/test/form/definitions/declaration-without-guidance.js'
|
|
14
16
|
import { getFormData, getFormState } from '~/test/helpers/component-helpers.js'
|
|
15
17
|
|
|
16
18
|
describe('DeclarationField', () => {
|
|
@@ -481,4 +483,26 @@ describe('DeclarationField', () => {
|
|
|
481
483
|
expect(DeclarationField.isBool(true)).toBe(true)
|
|
482
484
|
})
|
|
483
485
|
})
|
|
486
|
+
|
|
487
|
+
describe('Markdown header starting level', () => {
|
|
488
|
+
test('should determine startHeadingLevel is 3 some guidance', () => {
|
|
489
|
+
const modelDecl = new FormModel(declarationWithGuidance, {
|
|
490
|
+
basePath: 'test'
|
|
491
|
+
})
|
|
492
|
+
const field = modelDecl.componentMap.get(
|
|
493
|
+
'declarationField'
|
|
494
|
+
) as DeclarationField
|
|
495
|
+
expect(field.headerStartLevel).toBe(3)
|
|
496
|
+
})
|
|
497
|
+
|
|
498
|
+
test('should determine startHeadingLevel is 2 when no guidance', () => {
|
|
499
|
+
const modelDecl = new FormModel(declarationWithoutGuidance, {
|
|
500
|
+
basePath: 'test'
|
|
501
|
+
})
|
|
502
|
+
const field = modelDecl.componentMap.get(
|
|
503
|
+
'declarationField'
|
|
504
|
+
) as DeclarationField
|
|
505
|
+
expect(field.headerStartLevel).toBe(2)
|
|
506
|
+
})
|
|
507
|
+
})
|
|
484
508
|
})
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
ComponentType,
|
|
3
|
+
hasFormComponents,
|
|
4
|
+
isFormType,
|
|
5
|
+
type DeclarationFieldComponent,
|
|
6
|
+
type Item
|
|
7
|
+
} from '@defra/forms-model'
|
|
2
8
|
import joi, {
|
|
3
9
|
type ArraySchema,
|
|
4
10
|
type BooleanSchema,
|
|
@@ -30,6 +36,7 @@ export class DeclarationField extends FormComponent {
|
|
|
30
36
|
declare formSchema: ArraySchema<StringSchema[]>
|
|
31
37
|
declare stateSchema: BooleanSchema
|
|
32
38
|
declare content: string
|
|
39
|
+
headerStartLevel: number
|
|
33
40
|
|
|
34
41
|
constructor(
|
|
35
42
|
def: DeclarationFieldComponent,
|
|
@@ -64,6 +71,16 @@ export class DeclarationField extends FormComponent {
|
|
|
64
71
|
this.content = content
|
|
65
72
|
this.declarationConfirmationLabel =
|
|
66
73
|
options.declarationConfirmationLabel ?? this.DEFAULT_DECLARATION_LABEL
|
|
74
|
+
const formComponents = hasFormComponents(props.page?.pageDef)
|
|
75
|
+
? props.page.pageDef.components
|
|
76
|
+
: []
|
|
77
|
+
const numOfQuestionsOnPage = formComponents.filter((q) =>
|
|
78
|
+
isFormType(q.type)
|
|
79
|
+
).length
|
|
80
|
+
const hasGuidance = formComponents.some(
|
|
81
|
+
(comp, idx) => comp.type === ComponentType.Markdown && idx === 0
|
|
82
|
+
)
|
|
83
|
+
this.headerStartLevel = numOfQuestionsOnPage < 2 && !hasGuidance ? 2 : 3
|
|
67
84
|
}
|
|
68
85
|
|
|
69
86
|
getFormValueFromState(state: FormSubmissionState) {
|
|
@@ -133,7 +150,8 @@ export class DeclarationField extends FormComponent {
|
|
|
133
150
|
value: 'true',
|
|
134
151
|
checked: isChecked
|
|
135
152
|
}
|
|
136
|
-
]
|
|
153
|
+
],
|
|
154
|
+
headerStartLevel: this.headerStartLevel
|
|
137
155
|
}
|
|
138
156
|
}
|
|
139
157
|
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { ComponentType, type HiddenFieldComponent } from '@defra/forms-model'
|
|
2
|
+
|
|
3
|
+
import { ComponentCollection } from '~/src/server/plugins/engine/components/ComponentCollection.js'
|
|
4
|
+
import {
|
|
5
|
+
getAnswer,
|
|
6
|
+
type Field
|
|
7
|
+
} from '~/src/server/plugins/engine/components/helpers/components.js'
|
|
8
|
+
import { FormModel } from '~/src/server/plugins/engine/models/FormModel.js'
|
|
9
|
+
import definition from '~/test/form/definitions/blank.js'
|
|
10
|
+
import { getFormData, getFormState } from '~/test/helpers/component-helpers.js'
|
|
11
|
+
|
|
12
|
+
describe('HiddenField', () => {
|
|
13
|
+
let model: FormModel
|
|
14
|
+
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
model = new FormModel(definition, {
|
|
17
|
+
basePath: 'test'
|
|
18
|
+
})
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
describe('Defaults', () => {
|
|
22
|
+
let def: HiddenFieldComponent
|
|
23
|
+
let collection: ComponentCollection
|
|
24
|
+
let field: Field
|
|
25
|
+
|
|
26
|
+
beforeEach(() => {
|
|
27
|
+
def = {
|
|
28
|
+
title: 'Hidden field',
|
|
29
|
+
name: 'myComponent',
|
|
30
|
+
type: ComponentType.HiddenField,
|
|
31
|
+
options: {}
|
|
32
|
+
} satisfies HiddenFieldComponent
|
|
33
|
+
|
|
34
|
+
collection = new ComponentCollection([def], { model })
|
|
35
|
+
field = collection.fields[0]
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
describe('Schema', () => {
|
|
39
|
+
it('uses component title as label as default', () => {
|
|
40
|
+
const { formSchema } = collection
|
|
41
|
+
const { keys } = formSchema.describe()
|
|
42
|
+
|
|
43
|
+
expect(keys).toHaveProperty(
|
|
44
|
+
'myComponent',
|
|
45
|
+
expect.objectContaining({
|
|
46
|
+
flags: expect.objectContaining({
|
|
47
|
+
label: 'Hidden field'
|
|
48
|
+
})
|
|
49
|
+
})
|
|
50
|
+
)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('uses component name as keys', () => {
|
|
54
|
+
const { formSchema } = collection
|
|
55
|
+
const { keys } = formSchema.describe()
|
|
56
|
+
|
|
57
|
+
expect(field.keys).toEqual(['myComponent'])
|
|
58
|
+
expect(field.collection).toBeUndefined()
|
|
59
|
+
|
|
60
|
+
for (const key of field.keys) {
|
|
61
|
+
expect(keys).toHaveProperty(key)
|
|
62
|
+
}
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
it('is required by default', () => {
|
|
66
|
+
const { formSchema } = collection
|
|
67
|
+
const { keys } = formSchema.describe()
|
|
68
|
+
|
|
69
|
+
expect(keys).toHaveProperty(
|
|
70
|
+
'myComponent',
|
|
71
|
+
expect.objectContaining({
|
|
72
|
+
flags: expect.objectContaining({
|
|
73
|
+
presence: 'required'
|
|
74
|
+
})
|
|
75
|
+
})
|
|
76
|
+
)
|
|
77
|
+
})
|
|
78
|
+
it('accepts valid values', () => {
|
|
79
|
+
const result1 = collection.validate(getFormData('Hidden value'))
|
|
80
|
+
const result2 = collection.validate(getFormData('Hidden value 2'))
|
|
81
|
+
|
|
82
|
+
expect(result1.errors).toBeUndefined()
|
|
83
|
+
expect(result2.errors).toBeUndefined()
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
it('adds errors for empty value', () => {
|
|
87
|
+
const result = collection.validate(getFormData(''))
|
|
88
|
+
|
|
89
|
+
expect(result.errors).toEqual([
|
|
90
|
+
expect.objectContaining({
|
|
91
|
+
text: 'Enter hidden field'
|
|
92
|
+
})
|
|
93
|
+
])
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
it('adds errors for invalid values', () => {
|
|
97
|
+
const result1 = collection.validate(getFormData(['invalid']))
|
|
98
|
+
const result2 = collection.validate(
|
|
99
|
+
// @ts-expect-error - Allow invalid param for test
|
|
100
|
+
getFormData({ unknown: 'invalid' })
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
expect(result1.errors).toBeTruthy()
|
|
104
|
+
expect(result2.errors).toBeTruthy()
|
|
105
|
+
})
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
describe('State', () => {
|
|
109
|
+
it('returns text from state', () => {
|
|
110
|
+
const state1 = getFormState('Hidden field')
|
|
111
|
+
const state2 = getFormState(null)
|
|
112
|
+
|
|
113
|
+
const answer1 = getAnswer(field, state1)
|
|
114
|
+
const answer2 = getAnswer(field, state2)
|
|
115
|
+
|
|
116
|
+
expect(answer1).toBe('Hidden field')
|
|
117
|
+
expect(answer2).toBe('')
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
it('returns payload from state', () => {
|
|
121
|
+
const state1 = getFormState('Hidden field')
|
|
122
|
+
const state2 = getFormState(null)
|
|
123
|
+
|
|
124
|
+
const payload1 = field.getFormDataFromState(state1)
|
|
125
|
+
const payload2 = field.getFormDataFromState(state2)
|
|
126
|
+
|
|
127
|
+
expect(payload1).toEqual(getFormData('Hidden field'))
|
|
128
|
+
expect(payload2).toEqual(getFormData())
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
it('returns value from state', () => {
|
|
132
|
+
const state1 = getFormState('Hidden field')
|
|
133
|
+
const state2 = getFormState(null)
|
|
134
|
+
|
|
135
|
+
const value1 = field.getFormValueFromState(state1)
|
|
136
|
+
const value2 = field.getFormValueFromState(state2)
|
|
137
|
+
|
|
138
|
+
expect(value1).toBe('Hidden field')
|
|
139
|
+
expect(value2).toBeUndefined()
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
it('returns context for conditions and form submission', () => {
|
|
143
|
+
const state1 = getFormState('Hidden field')
|
|
144
|
+
const state2 = getFormState(null)
|
|
145
|
+
|
|
146
|
+
const value1 = field.getContextValueFromState(state1)
|
|
147
|
+
const value2 = field.getContextValueFromState(state2)
|
|
148
|
+
|
|
149
|
+
expect(value1).toBe('Hidden field')
|
|
150
|
+
expect(value2).toBeNull()
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
it('returns state from payload', () => {
|
|
154
|
+
const payload1 = getFormData('Hidden field')
|
|
155
|
+
const payload2 = getFormData()
|
|
156
|
+
|
|
157
|
+
const value1 = field.getStateFromValidForm(payload1)
|
|
158
|
+
const value2 = field.getStateFromValidForm(payload2)
|
|
159
|
+
|
|
160
|
+
expect(value1).toEqual(getFormState('Hidden field'))
|
|
161
|
+
expect(value2).toEqual(getFormState(null))
|
|
162
|
+
})
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
describe('View model', () => {
|
|
166
|
+
it('sets Nunjucks component defaults', () => {
|
|
167
|
+
const viewModel = field.getViewModel(getFormData('Hidden field'))
|
|
168
|
+
|
|
169
|
+
expect(viewModel).toEqual(
|
|
170
|
+
expect.objectContaining({
|
|
171
|
+
label: { text: def.title },
|
|
172
|
+
name: 'myComponent',
|
|
173
|
+
id: 'myComponent',
|
|
174
|
+
value: 'Hidden field'
|
|
175
|
+
})
|
|
176
|
+
)
|
|
177
|
+
})
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
describe('AllPossibleErrors', () => {
|
|
181
|
+
it('should return errors', () => {
|
|
182
|
+
const errors = field.getAllPossibleErrors()
|
|
183
|
+
expect(errors.baseErrors).not.toBeEmpty()
|
|
184
|
+
expect(errors.advancedSettingsErrors).toBeEmpty()
|
|
185
|
+
})
|
|
186
|
+
})
|
|
187
|
+
})
|
|
188
|
+
})
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type HiddenFieldComponent,
|
|
3
|
+
type TextFieldComponent
|
|
4
|
+
} from '@defra/forms-model'
|
|
5
|
+
import joi, { type StringSchema } from 'joi'
|
|
6
|
+
|
|
7
|
+
import { FormComponent } from '~/src/server/plugins/engine/components/FormComponent.js'
|
|
8
|
+
import { TextField } from '~/src/server/plugins/engine/components/TextField.js'
|
|
9
|
+
import { messageTemplate } from '~/src/server/plugins/engine/pageControllers/validationOptions.js'
|
|
10
|
+
import {
|
|
11
|
+
type ErrorMessageTemplateList,
|
|
12
|
+
type FormState,
|
|
13
|
+
type FormStateValue,
|
|
14
|
+
type FormSubmissionState
|
|
15
|
+
} from '~/src/server/plugins/engine/types.js'
|
|
16
|
+
|
|
17
|
+
export class HiddenField extends FormComponent {
|
|
18
|
+
declare formSchema: StringSchema
|
|
19
|
+
declare stateSchema: StringSchema
|
|
20
|
+
declare schema: TextFieldComponent['schema']
|
|
21
|
+
declare options: TextFieldComponent['options']
|
|
22
|
+
|
|
23
|
+
constructor(
|
|
24
|
+
def: HiddenFieldComponent,
|
|
25
|
+
props: ConstructorParameters<typeof FormComponent>[1]
|
|
26
|
+
) {
|
|
27
|
+
super(def, props)
|
|
28
|
+
|
|
29
|
+
const { options } = def
|
|
30
|
+
|
|
31
|
+
let formSchema = joi.string().trim().label(this.label).required()
|
|
32
|
+
|
|
33
|
+
if (options.required === false) {
|
|
34
|
+
formSchema = formSchema.allow('')
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
this.formSchema = formSchema.default('')
|
|
38
|
+
this.stateSchema = formSchema.default(null).allow(null)
|
|
39
|
+
this.schema = {}
|
|
40
|
+
this.options = {}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
getFormValueFromState(state: FormSubmissionState) {
|
|
44
|
+
const { name } = this
|
|
45
|
+
return this.getFormValue(state[name])
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
isValue(value?: FormStateValue | FormState): value is string {
|
|
49
|
+
return TextField.isText(value)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* For error preview page that shows all possible errors on a component
|
|
54
|
+
*/
|
|
55
|
+
getAllPossibleErrors(): ErrorMessageTemplateList {
|
|
56
|
+
return HiddenField.getAllPossibleErrors()
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Static version of getAllPossibleErrors that doesn't require a component instance.
|
|
61
|
+
*/
|
|
62
|
+
static getAllPossibleErrors(): ErrorMessageTemplateList {
|
|
63
|
+
return {
|
|
64
|
+
baseErrors: [{ type: 'required', template: messageTemplate.required }],
|
|
65
|
+
advancedSettingsErrors: []
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -5,6 +5,7 @@ import { ComponentBase } from '~/src/server/plugins/engine/components/ComponentB
|
|
|
5
5
|
export class Markdown extends ComponentBase {
|
|
6
6
|
declare options: MarkdownComponent['options']
|
|
7
7
|
content: MarkdownComponent['content']
|
|
8
|
+
headerStartLevel: number
|
|
8
9
|
|
|
9
10
|
constructor(
|
|
10
11
|
def: MarkdownComponent,
|
|
@@ -16,6 +17,7 @@ export class Markdown extends ComponentBase {
|
|
|
16
17
|
|
|
17
18
|
this.content = content
|
|
18
19
|
this.options = options
|
|
20
|
+
this.headerStartLevel = 2
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
getViewModel() {
|
|
@@ -23,7 +25,8 @@ export class Markdown extends ComponentBase {
|
|
|
23
25
|
|
|
24
26
|
return {
|
|
25
27
|
...viewModel,
|
|
26
|
-
content
|
|
28
|
+
content,
|
|
29
|
+
headerStartLevel: this.headerStartLevel
|
|
27
30
|
}
|
|
28
31
|
}
|
|
29
32
|
}
|
|
@@ -34,6 +34,7 @@ export type Field = InstanceType<
|
|
|
34
34
|
| typeof Components.TextField
|
|
35
35
|
| typeof Components.UkAddressField
|
|
36
36
|
| typeof Components.FileUploadField
|
|
37
|
+
| typeof Components.HiddenField
|
|
37
38
|
>
|
|
38
39
|
|
|
39
40
|
// Guidance component instances only
|
|
@@ -186,6 +187,10 @@ export function createComponent(
|
|
|
186
187
|
case ComponentType.LatLongField:
|
|
187
188
|
component = new Components.LatLongField(def, options)
|
|
188
189
|
break
|
|
190
|
+
|
|
191
|
+
case ComponentType.HiddenField:
|
|
192
|
+
component = new Components.HiddenField(def, options)
|
|
193
|
+
break
|
|
189
194
|
}
|
|
190
195
|
|
|
191
196
|
if (typeof component === 'undefined') {
|
|
@@ -2,6 +2,7 @@ import { ComponentType, type ComponentDef } from '@defra/forms-model'
|
|
|
2
2
|
|
|
3
3
|
import { ComponentBase } from '~/src/server/plugins/engine/components/ComponentBase.js'
|
|
4
4
|
import { EastingNorthingField } from '~/src/server/plugins/engine/components/EastingNorthingField.js'
|
|
5
|
+
import { HiddenField } from '~/src/server/plugins/engine/components/HiddenField.js'
|
|
5
6
|
import { LatLongField } from '~/src/server/plugins/engine/components/LatLongField.js'
|
|
6
7
|
import { NationalGridFieldNumberField } from '~/src/server/plugins/engine/components/NationalGridFieldNumberField.js'
|
|
7
8
|
import { OsGridRefField } from '~/src/server/plugins/engine/components/OsGridRefField.js'
|
|
@@ -96,6 +97,22 @@ describe('helpers tests', () => {
|
|
|
96
97
|
expect(component.name).toBe('testField')
|
|
97
98
|
expect(component.title).toBe('Test National Grid')
|
|
98
99
|
})
|
|
100
|
+
|
|
101
|
+
test('should create HiddenField component', () => {
|
|
102
|
+
const component = createComponent(
|
|
103
|
+
{
|
|
104
|
+
type: ComponentType.HiddenField,
|
|
105
|
+
name: 'hiddenField',
|
|
106
|
+
title: 'Hidden field',
|
|
107
|
+
options: {}
|
|
108
|
+
},
|
|
109
|
+
{ model: formModel }
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
expect(component).toBeInstanceOf(HiddenField)
|
|
113
|
+
expect(component.name).toBe('hiddenField')
|
|
114
|
+
expect(component.title).toBe('Hidden field')
|
|
115
|
+
})
|
|
99
116
|
})
|
|
100
117
|
|
|
101
118
|
describe('ComponentBase tests', () => {
|
|
@@ -28,3 +28,4 @@ export { EastingNorthingField } from '~/src/server/plugins/engine/components/Eas
|
|
|
28
28
|
export { OsGridRefField } from '~/src/server/plugins/engine/components/OsGridRefField.js'
|
|
29
29
|
export { NationalGridFieldNumberField } from '~/src/server/plugins/engine/components/NationalGridFieldNumberField.js'
|
|
30
30
|
export { LatLongField } from '~/src/server/plugins/engine/components/LatLongField.js'
|
|
31
|
+
export { HiddenField } from '~/src/server/plugins/engine/components/HiddenField.js'
|