@defra/forms-engine-plugin 4.0.23 → 4.0.25

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.
Files changed (48) hide show
  1. package/.server/server/forms/components.json +2 -2
  2. package/.server/server/plugins/engine/components/DeclarationField.d.ts +2 -0
  3. package/.server/server/plugins/engine/components/DeclarationField.js +8 -1
  4. package/.server/server/plugins/engine/components/DeclarationField.js.map +1 -1
  5. package/.server/server/plugins/engine/components/EastingNorthingField.js +7 -6
  6. package/.server/server/plugins/engine/components/EastingNorthingField.js.map +1 -1
  7. package/.server/server/plugins/engine/components/LatLongField.js +7 -6
  8. package/.server/server/plugins/engine/components/LatLongField.js.map +1 -1
  9. package/.server/server/plugins/engine/components/LocationFieldBase.d.ts +4 -4
  10. package/.server/server/plugins/engine/components/LocationFieldBase.js +3 -2
  11. package/.server/server/plugins/engine/components/LocationFieldBase.js.map +1 -1
  12. package/.server/server/plugins/engine/components/Markdown.d.ts +2 -0
  13. package/.server/server/plugins/engine/components/Markdown.js +4 -1
  14. package/.server/server/plugins/engine/components/Markdown.js.map +1 -1
  15. package/.server/server/plugins/engine/components/NationalGridFieldNumberField.d.ts +3 -3
  16. package/.server/server/plugins/engine/components/NationalGridFieldNumberField.js +5 -3
  17. package/.server/server/plugins/engine/components/NationalGridFieldNumberField.js.map +1 -1
  18. package/.server/server/plugins/engine/components/OsGridRefField.d.ts +3 -3
  19. package/.server/server/plugins/engine/components/OsGridRefField.js +5 -3
  20. package/.server/server/plugins/engine/components/OsGridRefField.js.map +1 -1
  21. package/.server/server/plugins/engine/components/helpers/index.d.ts +10 -0
  22. package/.server/server/plugins/engine/components/helpers/index.js +18 -0
  23. package/.server/server/plugins/engine/components/helpers/index.js.map +1 -1
  24. package/.server/server/plugins/engine/index.js +4 -1
  25. package/.server/server/plugins/engine/index.js.map +1 -1
  26. package/.server/server/plugins/engine/views/components/declarationfield.html +1 -1
  27. package/.server/server/plugins/engine/views/components/markdown.html +1 -1
  28. package/.server/server/plugins/engine/views/confirmation.html +1 -1
  29. package/package.json +3 -2
  30. package/src/server/forms/components.json +2 -2
  31. package/src/server/plugins/engine/components/DeclarationField.test.ts +24 -0
  32. package/src/server/plugins/engine/components/DeclarationField.ts +20 -2
  33. package/src/server/plugins/engine/components/EastingNorthingField.test.ts +30 -1
  34. package/src/server/plugins/engine/components/EastingNorthingField.ts +19 -6
  35. package/src/server/plugins/engine/components/LatLongField.test.ts +30 -1
  36. package/src/server/plugins/engine/components/LatLongField.ts +19 -6
  37. package/src/server/plugins/engine/components/LocationFieldBase.ts +11 -6
  38. package/src/server/plugins/engine/components/Markdown.ts +4 -1
  39. package/src/server/plugins/engine/components/NationalGridFieldNumberField.test.ts +4 -4
  40. package/src/server/plugins/engine/components/NationalGridFieldNumberField.ts +11 -4
  41. package/src/server/plugins/engine/components/OsGridRefField.test.ts +4 -4
  42. package/src/server/plugins/engine/components/OsGridRefField.ts +11 -3
  43. package/src/server/plugins/engine/components/helpers/helpers.test.ts +40 -0
  44. package/src/server/plugins/engine/components/helpers/index.ts +18 -0
  45. package/src/server/plugins/engine/index.ts +5 -2
  46. package/src/server/plugins/engine/views/components/declarationfield.html +1 -1
  47. package/src/server/plugins/engine/views/components/markdown.html +1 -1
  48. package/src/server/plugins/engine/views/confirmation.html +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"OsGridRefField.js","names":["LocationFieldBase","OsGridRefField","getValidationConfig","pattern","patternErrorMessage","requiredMessage","getErrorTemplates","type","template","getAllPossibleErrors","instance","Object","create","prototype"],"sources":["../../../../../src/server/plugins/engine/components/OsGridRefField.ts"],"sourcesContent":["import { type OsGridRefFieldComponent } from '@defra/forms-model'\n\nimport { LocationFieldBase } from '~/src/server/plugins/engine/components/LocationFieldBase.js'\n\nexport class OsGridRefField extends LocationFieldBase {\n declare options: OsGridRefFieldComponent['options']\n\n protected getValidationConfig() {\n // Regex for OS national grid references (NGR)\n // Validates specific valid OS grid letter combinations with:\n // - 2 letters & 6 digits in 2 blocks of 3 e.g. ST 678 678\n // - 2 letters & 8 digits in 2 blocks of 4 e.g. ST 6789 6789\n // - 2 letters & 10 digits in 2 blocks of 5 e.g. SO 12345 12345\n // Optional spaces between each block\n const pattern =\n /^((([sS]|[nN])[a-hA-Hj-zJ-Z])|(([tT]|[oO])[abfglmqrvwABFGLMQRVW])|([hH][l-zL-Z])|([jJ][lmqrvwLMQRVW]))\\s?(([0-9]{3})\\s?([0-9]{3})|([0-9]{4})\\s?([0-9]{4})|([0-9]{5})\\s?([0-9]{5}))$/\n\n return {\n pattern,\n patternErrorMessage: `Enter a valid OS grid reference for {{#title}} like TQ123456`,\n requiredMessage: 'Enter {{#title}}'\n }\n }\n\n protected getErrorTemplates() {\n return [\n {\n type: 'pattern',\n template: 'Enter a valid OS grid reference for {{#title}} like TQ123456'\n }\n ]\n }\n\n /**\n * Static version of getAllPossibleErrors that doesn't require a component instance.\n */\n static getAllPossibleErrors() {\n const instance = Object.create(OsGridRefField.prototype) as OsGridRefField\n return instance.getAllPossibleErrors()\n }\n}\n"],"mappings":"AAEA,SAASA,iBAAiB;AAE1B,OAAO,MAAMC,cAAc,SAASD,iBAAiB,CAAC;EAG1CE,mBAAmBA,CAAA,EAAG;IAC9B;IACA;IACA;IACA;IACA;IACA;IACA,MAAMC,OAAO,GACX,qLAAqL;IAEvL,OAAO;MACLA,OAAO;MACPC,mBAAmB,EAAE,8DAA8D;MACnFC,eAAe,EAAE;IACnB,CAAC;EACH;EAEUC,iBAAiBA,CAAA,EAAG;IAC5B,OAAO,CACL;MACEC,IAAI,EAAE,SAAS;MACfC,QAAQ,EAAE;IACZ,CAAC,CACF;EACH;;EAEA;AACF;AACA;EACE,OAAOC,oBAAoBA,CAAA,EAAG;IAC5B,MAAMC,QAAQ,GAAGC,MAAM,CAACC,MAAM,CAACX,cAAc,CAACY,SAAS,CAAmB;IAC1E,OAAOH,QAAQ,CAACD,oBAAoB,CAAC,CAAC;EACxC;AACF","ignoreList":[]}
1
+ {"version":3,"file":"OsGridRefField.js","names":["LocationFieldBase","createLowerFirstExpression","OsGridRefField","getValidationConfig","pattern","patternTemplate","patternErrorMessage","requiredMessage","getErrorTemplates","type","template","getAllPossibleErrors","instance","Object","create","prototype"],"sources":["../../../../../src/server/plugins/engine/components/OsGridRefField.ts"],"sourcesContent":["import { type OsGridRefFieldComponent } from '@defra/forms-model'\n\nimport { LocationFieldBase } from '~/src/server/plugins/engine/components/LocationFieldBase.js'\nimport { createLowerFirstExpression } from '~/src/server/plugins/engine/components/helpers/index.js'\n\nexport class OsGridRefField extends LocationFieldBase {\n declare options: OsGridRefFieldComponent['options']\n\n protected getValidationConfig() {\n // Regex for OS national grid references (NGR)\n // Validates specific valid OS grid letter combinations with:\n // - 2 letters & 6 digits in 2 blocks of 3 e.g. ST 678 678\n // - 2 letters & 8 digits in 2 blocks of 4 e.g. ST 6789 6789\n // - 2 letters & 10 digits in 2 blocks of 5 e.g. SO 12345 12345\n // Optional spaces between each block\n const pattern =\n /^((([sS]|[nN])[a-hA-Hj-zJ-Z])|(([tT]|[oO])[abfglmqrvwABFGLMQRVW])|([hH][l-zL-Z])|([jJ][lmqrvwLMQRVW]))\\s?(([0-9]{3})\\s?([0-9]{3})|([0-9]{4})\\s?([0-9]{4})|([0-9]{5})\\s?([0-9]{5}))$/\n\n const patternTemplate =\n 'Enter a valid OS grid reference for {{lowerFirst(#title)}} like TQ123456'\n\n return {\n pattern,\n patternErrorMessage: createLowerFirstExpression(patternTemplate),\n requiredMessage: createLowerFirstExpression(\n 'Enter {{lowerFirst(#title)}}'\n )\n }\n }\n\n protected getErrorTemplates() {\n return [\n {\n type: 'pattern',\n template: createLowerFirstExpression(\n 'Enter a valid OS grid reference for {{lowerFirst(#title)}} like TQ123456'\n )\n }\n ]\n }\n\n /**\n * Static version of getAllPossibleErrors that doesn't require a component instance.\n */\n static getAllPossibleErrors() {\n const instance = Object.create(OsGridRefField.prototype) as OsGridRefField\n return instance.getAllPossibleErrors()\n }\n}\n"],"mappings":"AAEA,SAASA,iBAAiB;AAC1B,SAASC,0BAA0B;AAEnC,OAAO,MAAMC,cAAc,SAASF,iBAAiB,CAAC;EAG1CG,mBAAmBA,CAAA,EAAG;IAC9B;IACA;IACA;IACA;IACA;IACA;IACA,MAAMC,OAAO,GACX,qLAAqL;IAEvL,MAAMC,eAAe,GACnB,0EAA0E;IAE5E,OAAO;MACLD,OAAO;MACPE,mBAAmB,EAAEL,0BAA0B,CAACI,eAAe,CAAC;MAChEE,eAAe,EAAEN,0BAA0B,CACzC,8BACF;IACF,CAAC;EACH;EAEUO,iBAAiBA,CAAA,EAAG;IAC5B,OAAO,CACL;MACEC,IAAI,EAAE,SAAS;MACfC,QAAQ,EAAET,0BAA0B,CAClC,0EACF;IACF,CAAC,CACF;EACH;;EAEA;AACF;AACA;EACE,OAAOU,oBAAoBA,CAAA,EAAG;IAC5B,MAAMC,QAAQ,GAAGC,MAAM,CAACC,MAAM,CAACZ,cAAc,CAACa,SAAS,CAAmB;IAC1E,OAAOH,QAAQ,CAACD,oBAAoB,CAAC,CAAC;EACxC;AACF","ignoreList":[]}
@@ -1,4 +1,5 @@
1
1
  import { type ComponentDef } from '@defra/forms-model';
2
+ import { type JoiExpression, type ReferenceOptions } from 'joi';
2
3
  /**
3
4
  * Prevent Markdown formatting
4
5
  * @see {@link https://pandoc.org/chunkedhtml-demo/8.11-backslash-escapes.html}
@@ -9,3 +10,12 @@ export declare const addClassOptionIfNone: (options: Extract<ComponentDef, {
9
10
  classes?: string;
10
11
  };
11
12
  }>["options"], className: string) => void;
13
+ /**
14
+ * Configuration for Joi expressions that use lowerFirst function
15
+ */
16
+ export declare const lowerFirstExpressionOptions: ReferenceOptions;
17
+ /**
18
+ * Creates a Joi expression with lowerFirst function support
19
+ * Used for error messages in location field components
20
+ */
21
+ export declare const createLowerFirstExpression: (template: string) => JoiExpression;
@@ -1,3 +1,6 @@
1
+ import joi from 'joi';
2
+ import lowerFirst from 'lodash/lowerFirst.js';
3
+
1
4
  /**
2
5
  * Prevent Markdown formatting
3
6
  * @see {@link https://pandoc.org/chunkedhtml-demo/8.11-backslash-escapes.html}
@@ -12,4 +15,19 @@ export function escapeMarkdown(answer) {
12
15
  export const addClassOptionIfNone = (options, className) => {
13
16
  options.classes ??= className;
14
17
  };
18
+
19
+ /**
20
+ * Configuration for Joi expressions that use lowerFirst function
21
+ */
22
+ export const lowerFirstExpressionOptions = {
23
+ functions: {
24
+ lowerFirst
25
+ }
26
+ };
27
+
28
+ /**
29
+ * Creates a Joi expression with lowerFirst function support
30
+ * Used for error messages in location field components
31
+ */
32
+ export const createLowerFirstExpression = template => joi.expression(template, lowerFirstExpressionOptions);
15
33
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["escapeMarkdown","answer","punctuation","character","replaceAll","addClassOptionIfNone","options","className","classes"],"sources":["../../../../../../src/server/plugins/engine/components/helpers/index.ts"],"sourcesContent":["import { type ComponentDef } from '@defra/forms-model'\n\n/**\n * Prevent Markdown formatting\n * @see {@link https://pandoc.org/chunkedhtml-demo/8.11-backslash-escapes.html}\n */\nexport function escapeMarkdown(answer: string) {\n const punctuation = [\n '`',\n \"'\",\n '*',\n '_',\n '{',\n '}',\n '[',\n ']',\n '(',\n ')',\n '#',\n '+',\n '-',\n '.',\n '!'\n ]\n\n for (const character of punctuation) {\n answer = answer.replaceAll(character, `\\\\${character}`)\n }\n\n return answer\n}\n\nexport const addClassOptionIfNone = (\n options: Extract<ComponentDef, { options: { classes?: string } }>['options'],\n className: string\n) => {\n options.classes ??= className\n}\n"],"mappings":"AAEA;AACA;AACA;AACA;AACA,OAAO,SAASA,cAAcA,CAACC,MAAc,EAAE;EAC7C,MAAMC,WAAW,GAAG,CAClB,GAAG,EACH,GAAG,EACH,GAAG,EACH,GAAG,EACH,GAAG,EACH,GAAG,EACH,GAAG,EACH,GAAG,EACH,GAAG,EACH,GAAG,EACH,GAAG,EACH,GAAG,EACH,GAAG,EACH,GAAG,EACH,GAAG,CACJ;EAED,KAAK,MAAMC,SAAS,IAAID,WAAW,EAAE;IACnCD,MAAM,GAAGA,MAAM,CAACG,UAAU,CAACD,SAAS,EAAE,KAAKA,SAAS,EAAE,CAAC;EACzD;EAEA,OAAOF,MAAM;AACf;AAEA,OAAO,MAAMI,oBAAoB,GAAGA,CAClCC,OAA4E,EAC5EC,SAAiB,KACd;EACHD,OAAO,CAACE,OAAO,KAAKD,SAAS;AAC/B,CAAC","ignoreList":[]}
1
+ {"version":3,"file":"index.js","names":["joi","lowerFirst","escapeMarkdown","answer","punctuation","character","replaceAll","addClassOptionIfNone","options","className","classes","lowerFirstExpressionOptions","functions","createLowerFirstExpression","template","expression"],"sources":["../../../../../../src/server/plugins/engine/components/helpers/index.ts"],"sourcesContent":["import { type ComponentDef } from '@defra/forms-model'\nimport joi, { type JoiExpression, type ReferenceOptions } from 'joi'\nimport lowerFirst from 'lodash/lowerFirst.js'\n\n/**\n * Prevent Markdown formatting\n * @see {@link https://pandoc.org/chunkedhtml-demo/8.11-backslash-escapes.html}\n */\nexport function escapeMarkdown(answer: string) {\n const punctuation = [\n '`',\n \"'\",\n '*',\n '_',\n '{',\n '}',\n '[',\n ']',\n '(',\n ')',\n '#',\n '+',\n '-',\n '.',\n '!'\n ]\n\n for (const character of punctuation) {\n answer = answer.replaceAll(character, `\\\\${character}`)\n }\n\n return answer\n}\n\nexport const addClassOptionIfNone = (\n options: Extract<ComponentDef, { options: { classes?: string } }>['options'],\n className: string\n) => {\n options.classes ??= className\n}\n\n/**\n * Configuration for Joi expressions that use lowerFirst function\n */\nexport const lowerFirstExpressionOptions = {\n functions: {\n lowerFirst\n }\n} as ReferenceOptions\n\n/**\n * Creates a Joi expression with lowerFirst function support\n * Used for error messages in location field components\n */\nexport const createLowerFirstExpression = (template: string): JoiExpression =>\n joi.expression(template, lowerFirstExpressionOptions) as JoiExpression\n"],"mappings":"AACA,OAAOA,GAAG,MAAqD,KAAK;AACpE,OAAOC,UAAU,MAAM,sBAAsB;;AAE7C;AACA;AACA;AACA;AACA,OAAO,SAASC,cAAcA,CAACC,MAAc,EAAE;EAC7C,MAAMC,WAAW,GAAG,CAClB,GAAG,EACH,GAAG,EACH,GAAG,EACH,GAAG,EACH,GAAG,EACH,GAAG,EACH,GAAG,EACH,GAAG,EACH,GAAG,EACH,GAAG,EACH,GAAG,EACH,GAAG,EACH,GAAG,EACH,GAAG,EACH,GAAG,CACJ;EAED,KAAK,MAAMC,SAAS,IAAID,WAAW,EAAE;IACnCD,MAAM,GAAGA,MAAM,CAACG,UAAU,CAACD,SAAS,EAAE,KAAKA,SAAS,EAAE,CAAC;EACzD;EAEA,OAAOF,MAAM;AACf;AAEA,OAAO,MAAMI,oBAAoB,GAAGA,CAClCC,OAA4E,EAC5EC,SAAiB,KACd;EACHD,OAAO,CAACE,OAAO,KAAKD,SAAS;AAC/B,CAAC;;AAED;AACA;AACA;AACA,OAAO,MAAME,2BAA2B,GAAG;EACzCC,SAAS,EAAE;IACTX;EACF;AACF,CAAqB;;AAErB;AACA;AACA;AACA;AACA,OAAO,MAAMY,0BAA0B,GAAIC,QAAgB,IACzDd,GAAG,CAACe,UAAU,CAACD,QAAQ,EAAEH,2BAA2B,CAAkB","ignoreList":[]}
@@ -17,7 +17,10 @@ export const prepareNunjucksEnvironment = function (env, pluginOptions) {
17
17
  for (const [name, nunjucksFilter] of Object.entries(filters)) {
18
18
  env.addFilter(name, nunjucksFilter);
19
19
  }
20
- env.addFilter('markdown', text => markdownToHtml(text, pluginOptions.baseUrl));
20
+ env.addFilter('markdown', (text, startingHeaderLevel) => markdownToHtml(text, {
21
+ baseUrl: pluginOptions.baseUrl,
22
+ startingHeaderLevel
23
+ }));
21
24
  for (const [name, nunjucksGlobal] of Object.entries(globals)) {
22
25
  env.addGlobal(name, nunjucksGlobal);
23
26
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["markdownToHtml","engine","plugin","checkComponentTemplates","checkErrorTemplates","evaluate","govukRebrand","filters","getPageHref","context","globals","VIEW_PATH","PLUGIN_PATH","prepareNunjucksEnvironment","env","pluginOptions","name","nunjucksFilter","Object","entries","addFilter","text","baseUrl","nunjucksGlobal","addGlobal","filter","registerFilter","forEach","fn"],"sources":["../../../../src/server/plugins/engine/index.ts"],"sourcesContent":["import { markdownToHtml } from '@defra/forms-model'\nimport { type Environment } from 'nunjucks'\n\nimport { engine } from '~/src/server/plugins/engine/helpers.js'\nimport { plugin } from '~/src/server/plugins/engine/plugin.js'\nimport { type PluginOptions } from '~/src/server/plugins/engine/types.js'\nimport {\n checkComponentTemplates,\n checkErrorTemplates,\n evaluate,\n govukRebrand\n} from '~/src/server/plugins/nunjucks/environment.js'\nimport * as filters from '~/src/server/plugins/nunjucks/filters/index.js'\n\nexport { getPageHref } from '~/src/server/plugins/engine/helpers.js'\nexport { context } from '~/src/server/plugins/nunjucks/context.js'\n\nconst globals = {\n checkComponentTemplates,\n checkErrorTemplates,\n evaluate,\n govukRebrand\n}\n\nexport const VIEW_PATH = 'src/server/plugins/engine/views'\nexport const PLUGIN_PATH = 'node_modules/@defra/forms-engine-plugin'\n\nexport const prepareNunjucksEnvironment = function (\n env: Environment,\n pluginOptions: PluginOptions\n) {\n for (const [name, nunjucksFilter] of Object.entries(filters)) {\n env.addFilter(name, nunjucksFilter)\n }\n\n env.addFilter('markdown', (text: string) =>\n markdownToHtml(text, pluginOptions.baseUrl)\n )\n\n for (const [name, nunjucksGlobal] of Object.entries(globals)) {\n env.addGlobal(name, nunjucksGlobal)\n }\n\n // Apply any additional filters to both the liquid and nunjucks engines\n if (pluginOptions.filters) {\n for (const [name, filter] of Object.entries(pluginOptions.filters)) {\n env.addFilter(name, filter)\n engine.registerFilter(name, filter)\n }\n }\n\n // Apply any additional globals to nunjucks engines\n if (pluginOptions.globals) {\n Object.entries(pluginOptions.globals).forEach(([name, fn]) => {\n env.addGlobal(name, fn)\n })\n }\n}\n\nexport default plugin\n"],"mappings":"AAAA,SAASA,cAAc,QAAQ,oBAAoB;AAGnD,SAASC,MAAM;AACf,SAASC,MAAM;AAEf,SACEC,uBAAuB,EACvBC,mBAAmB,EACnBC,QAAQ,EACRC,YAAY;AAEd,OAAO,KAAKC,OAAO;AAEnB,SAASC,WAAW;AACpB,SAASC,OAAO;AAEhB,MAAMC,OAAO,GAAG;EACdP,uBAAuB;EACvBC,mBAAmB;EACnBC,QAAQ;EACRC;AACF,CAAC;AAED,OAAO,MAAMK,SAAS,GAAG,iCAAiC;AAC1D,OAAO,MAAMC,WAAW,GAAG,yCAAyC;AAEpE,OAAO,MAAMC,0BAA0B,GAAG,SAAAA,CACxCC,GAAgB,EAChBC,aAA4B,EAC5B;EACA,KAAK,MAAM,CAACC,IAAI,EAAEC,cAAc,CAAC,IAAIC,MAAM,CAACC,OAAO,CAACZ,OAAO,CAAC,EAAE;IAC5DO,GAAG,CAACM,SAAS,CAACJ,IAAI,EAAEC,cAAc,CAAC;EACrC;EAEAH,GAAG,CAACM,SAAS,CAAC,UAAU,EAAGC,IAAY,IACrCrB,cAAc,CAACqB,IAAI,EAAEN,aAAa,CAACO,OAAO,CAC5C,CAAC;EAED,KAAK,MAAM,CAACN,IAAI,EAAEO,cAAc,CAAC,IAAIL,MAAM,CAACC,OAAO,CAACT,OAAO,CAAC,EAAE;IAC5DI,GAAG,CAACU,SAAS,CAACR,IAAI,EAAEO,cAAc,CAAC;EACrC;;EAEA;EACA,IAAIR,aAAa,CAACR,OAAO,EAAE;IACzB,KAAK,MAAM,CAACS,IAAI,EAAES,MAAM,CAAC,IAAIP,MAAM,CAACC,OAAO,CAACJ,aAAa,CAACR,OAAO,CAAC,EAAE;MAClEO,GAAG,CAACM,SAAS,CAACJ,IAAI,EAAES,MAAM,CAAC;MAC3BxB,MAAM,CAACyB,cAAc,CAACV,IAAI,EAAES,MAAM,CAAC;IACrC;EACF;;EAEA;EACA,IAAIV,aAAa,CAACL,OAAO,EAAE;IACzBQ,MAAM,CAACC,OAAO,CAACJ,aAAa,CAACL,OAAO,CAAC,CAACiB,OAAO,CAAC,CAAC,CAACX,IAAI,EAAEY,EAAE,CAAC,KAAK;MAC5Dd,GAAG,CAACU,SAAS,CAACR,IAAI,EAAEY,EAAE,CAAC;IACzB,CAAC,CAAC;EACJ;AACF,CAAC;AAED,eAAe1B,MAAM","ignoreList":[]}
1
+ {"version":3,"file":"index.js","names":["markdownToHtml","engine","plugin","checkComponentTemplates","checkErrorTemplates","evaluate","govukRebrand","filters","getPageHref","context","globals","VIEW_PATH","PLUGIN_PATH","prepareNunjucksEnvironment","env","pluginOptions","name","nunjucksFilter","Object","entries","addFilter","text","startingHeaderLevel","baseUrl","nunjucksGlobal","addGlobal","filter","registerFilter","forEach","fn"],"sources":["../../../../src/server/plugins/engine/index.ts"],"sourcesContent":["import { markdownToHtml } from '@defra/forms-model'\nimport { type Environment } from 'nunjucks'\n\nimport { engine } from '~/src/server/plugins/engine/helpers.js'\nimport { plugin } from '~/src/server/plugins/engine/plugin.js'\nimport { type PluginOptions } from '~/src/server/plugins/engine/types.js'\nimport {\n checkComponentTemplates,\n checkErrorTemplates,\n evaluate,\n govukRebrand\n} from '~/src/server/plugins/nunjucks/environment.js'\nimport * as filters from '~/src/server/plugins/nunjucks/filters/index.js'\n\nexport { getPageHref } from '~/src/server/plugins/engine/helpers.js'\nexport { context } from '~/src/server/plugins/nunjucks/context.js'\n\nconst globals = {\n checkComponentTemplates,\n checkErrorTemplates,\n evaluate,\n govukRebrand\n}\n\nexport const VIEW_PATH = 'src/server/plugins/engine/views'\nexport const PLUGIN_PATH = 'node_modules/@defra/forms-engine-plugin'\n\nexport const prepareNunjucksEnvironment = function (\n env: Environment,\n pluginOptions: PluginOptions\n) {\n for (const [name, nunjucksFilter] of Object.entries(filters)) {\n env.addFilter(name, nunjucksFilter)\n }\n\n env.addFilter('markdown', (text: string, startingHeaderLevel?: number) =>\n markdownToHtml(text, {\n baseUrl: pluginOptions.baseUrl,\n startingHeaderLevel\n })\n )\n\n for (const [name, nunjucksGlobal] of Object.entries(globals)) {\n env.addGlobal(name, nunjucksGlobal)\n }\n\n // Apply any additional filters to both the liquid and nunjucks engines\n if (pluginOptions.filters) {\n for (const [name, filter] of Object.entries(pluginOptions.filters)) {\n env.addFilter(name, filter)\n engine.registerFilter(name, filter)\n }\n }\n\n // Apply any additional globals to nunjucks engines\n if (pluginOptions.globals) {\n Object.entries(pluginOptions.globals).forEach(([name, fn]) => {\n env.addGlobal(name, fn)\n })\n }\n}\n\nexport default plugin\n"],"mappings":"AAAA,SAASA,cAAc,QAAQ,oBAAoB;AAGnD,SAASC,MAAM;AACf,SAASC,MAAM;AAEf,SACEC,uBAAuB,EACvBC,mBAAmB,EACnBC,QAAQ,EACRC,YAAY;AAEd,OAAO,KAAKC,OAAO;AAEnB,SAASC,WAAW;AACpB,SAASC,OAAO;AAEhB,MAAMC,OAAO,GAAG;EACdP,uBAAuB;EACvBC,mBAAmB;EACnBC,QAAQ;EACRC;AACF,CAAC;AAED,OAAO,MAAMK,SAAS,GAAG,iCAAiC;AAC1D,OAAO,MAAMC,WAAW,GAAG,yCAAyC;AAEpE,OAAO,MAAMC,0BAA0B,GAAG,SAAAA,CACxCC,GAAgB,EAChBC,aAA4B,EAC5B;EACA,KAAK,MAAM,CAACC,IAAI,EAAEC,cAAc,CAAC,IAAIC,MAAM,CAACC,OAAO,CAACZ,OAAO,CAAC,EAAE;IAC5DO,GAAG,CAACM,SAAS,CAACJ,IAAI,EAAEC,cAAc,CAAC;EACrC;EAEAH,GAAG,CAACM,SAAS,CAAC,UAAU,EAAE,CAACC,IAAY,EAAEC,mBAA4B,KACnEtB,cAAc,CAACqB,IAAI,EAAE;IACnBE,OAAO,EAAER,aAAa,CAACQ,OAAO;IAC9BD;EACF,CAAC,CACH,CAAC;EAED,KAAK,MAAM,CAACN,IAAI,EAAEQ,cAAc,CAAC,IAAIN,MAAM,CAACC,OAAO,CAACT,OAAO,CAAC,EAAE;IAC5DI,GAAG,CAACW,SAAS,CAACT,IAAI,EAAEQ,cAAc,CAAC;EACrC;;EAEA;EACA,IAAIT,aAAa,CAACR,OAAO,EAAE;IACzB,KAAK,MAAM,CAACS,IAAI,EAAEU,MAAM,CAAC,IAAIR,MAAM,CAACC,OAAO,CAACJ,aAAa,CAACR,OAAO,CAAC,EAAE;MAClEO,GAAG,CAACM,SAAS,CAACJ,IAAI,EAAEU,MAAM,CAAC;MAC3BzB,MAAM,CAAC0B,cAAc,CAACX,IAAI,EAAEU,MAAM,CAAC;IACrC;EACF;;EAEA;EACA,IAAIX,aAAa,CAACL,OAAO,EAAE;IACzBQ,MAAM,CAACC,OAAO,CAACJ,aAAa,CAACL,OAAO,CAAC,CAACkB,OAAO,CAAC,CAAC,CAACZ,IAAI,EAAEa,EAAE,CAAC,KAAK;MAC5Df,GAAG,CAACW,SAAS,CAACT,IAAI,EAAEa,EAAE,CAAC;IACzB,CAAC,CAAC;EACJ;AACF,CAAC;AAED,eAAe3B,MAAM","ignoreList":[]}
@@ -5,7 +5,7 @@
5
5
  {% macro DeclarationField(component) %}
6
6
  {% set content %}
7
7
  <div class="app-prose-scope">
8
- {{ component.model.content | markdown | safe }}
8
+ {{ component.model.content | markdown(component.model.headerStartLevel) | safe }}
9
9
  </div>
10
10
  {% endset %}
11
11
  {% set checkboxes = component.model | merge({ formGroup: { beforeInputs: { html: content } } }) %}
@@ -1,5 +1,5 @@
1
1
  {% macro Markdown(component) %}
2
2
  <div class="app-prose-scope">
3
- {{ component.model.content | markdown | safe }}
3
+ {{ component.model.content | markdown(component.model.headerStartLevel) | safe }}
4
4
  </div>
5
5
  {% endmacro %}
@@ -12,7 +12,7 @@
12
12
  }) }}
13
13
  <h2 class="govuk-heading-m">What happens next</h2>
14
14
  <div class="app-prose-scope">
15
- {{ submissionGuidance | markdown | safe }}
15
+ {{ submissionGuidance | markdown(3) | safe }}
16
16
  </div>
17
17
  </div>
18
18
  </div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@defra/forms-engine-plugin",
3
- "version": "4.0.23",
3
+ "version": "4.0.25",
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.580",
73
+ "@defra/forms-model": "^3.0.584",
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 a H3 in markdown\n\n[An internal link](http://localhost:3009/fictional-page)\n\n[An external link](https://defra.gov.uk/fictional-page)",
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": "By 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",
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 { type DeclarationFieldComponent, type Item } from '@defra/forms-model'
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
 
@@ -416,7 +416,36 @@ describe('EastingNorthingField', () => {
416
416
  const staticResult = EastingNorthingField.getAllPossibleErrors()
417
417
  const instanceResult = field.getAllPossibleErrors()
418
418
 
419
- expect(instanceResult).toEqual(staticResult)
419
+ // Compare structure and content
420
+ expect(instanceResult.baseErrors).toHaveLength(
421
+ staticResult.baseErrors.length
422
+ )
423
+ expect(instanceResult.advancedSettingsErrors).toHaveLength(
424
+ staticResult.advancedSettingsErrors.length
425
+ )
426
+
427
+ // Compare error types
428
+ expect(instanceResult.baseErrors.map((e) => e.type)).toEqual(
429
+ staticResult.baseErrors.map((e) => e.type)
430
+ )
431
+ expect(
432
+ instanceResult.advancedSettingsErrors.map((e) => e.type)
433
+ ).toEqual(staticResult.advancedSettingsErrors.map((e) => e.type))
434
+
435
+ // Compare rendered templates
436
+ expect(
437
+ instanceResult.baseErrors.map((e) =>
438
+ typeof e.template === 'object' && 'rendered' in e.template
439
+ ? e.template.rendered
440
+ : e.template
441
+ )
442
+ ).toEqual(
443
+ staticResult.baseErrors.map((e) =>
444
+ typeof e.template === 'object' && 'rendered' in e.template
445
+ ? e.template.rendered
446
+ : e.template
447
+ )
448
+ )
420
449
  })
421
450
  })
422
451
  })
@@ -15,6 +15,7 @@ import {
15
15
  getLocationFieldViewModel
16
16
  } from '~/src/server/plugins/engine/components/LocationFieldHelpers.js'
17
17
  import { NumberField } from '~/src/server/plugins/engine/components/NumberField.js'
18
+ import { createLowerFirstExpression } from '~/src/server/plugins/engine/components/helpers/index.js'
18
19
  import { type EastingNorthingState } from '~/src/server/plugins/engine/components/types.js'
19
20
  import { messageTemplate } from '~/src/server/plugins/engine/pageControllers/validationOptions.js'
20
21
  import {
@@ -198,29 +199,41 @@ export class EastingNorthingField extends FormComponent {
198
199
  { type: 'required', template: messageTemplate.required },
199
200
  {
200
201
  type: 'eastingFormat',
201
- template: 'Easting for {{#title}} must be between 1 and 6 digits'
202
+ template: createLowerFirstExpression(
203
+ 'Easting for {{lowerFirst(#title)}} must be between 1 and 6 digits'
204
+ )
202
205
  },
203
206
  {
204
207
  type: 'northingFormat',
205
- template: 'Northing for {{#title}} must be between 1 and 7 digits'
208
+ template: createLowerFirstExpression(
209
+ 'Northing for {{lowerFirst(#title)}} must be between 1 and 7 digits'
210
+ )
206
211
  }
207
212
  ],
208
213
  advancedSettingsErrors: [
209
214
  {
210
215
  type: 'eastingMin',
211
- template: `Easting for {{#title}} must be between ${DEFAULT_EASTING_MIN} and ${DEFAULT_EASTING_MAX}`
216
+ template: createLowerFirstExpression(
217
+ `Easting for {{lowerFirst(#title)}} must be between ${DEFAULT_EASTING_MIN} and ${DEFAULT_EASTING_MAX}`
218
+ )
212
219
  },
213
220
  {
214
221
  type: 'eastingMax',
215
- template: `Easting for {{#title}} must be between ${DEFAULT_EASTING_MIN} and ${DEFAULT_EASTING_MAX}`
222
+ template: createLowerFirstExpression(
223
+ `Easting for {{lowerFirst(#title)}} must be between ${DEFAULT_EASTING_MIN} and ${DEFAULT_EASTING_MAX}`
224
+ )
216
225
  },
217
226
  {
218
227
  type: 'northingMin',
219
- template: `Northing for {{#title}} must be between ${DEFAULT_NORTHING_MIN} and ${DEFAULT_NORTHING_MAX}`
228
+ template: createLowerFirstExpression(
229
+ `Northing for {{lowerFirst(#title)}} must be between ${DEFAULT_NORTHING_MIN} and ${DEFAULT_NORTHING_MAX}`
230
+ )
220
231
  },
221
232
  {
222
233
  type: 'northingMax',
223
- template: `Northing for {{#title}} must be between ${DEFAULT_NORTHING_MIN} and ${DEFAULT_NORTHING_MAX}`
234
+ template: createLowerFirstExpression(
235
+ `Northing for {{lowerFirst(#title)}} must be between ${DEFAULT_NORTHING_MIN} and ${DEFAULT_NORTHING_MAX}`
236
+ )
224
237
  }
225
238
  ]
226
239
  }
@@ -404,7 +404,36 @@ describe('LatLongField', () => {
404
404
  const staticResult = LatLongField.getAllPossibleErrors()
405
405
  const instanceResult = field.getAllPossibleErrors()
406
406
 
407
- expect(instanceResult).toEqual(staticResult)
407
+ // Compare structure and content
408
+ expect(instanceResult.baseErrors).toHaveLength(
409
+ staticResult.baseErrors.length
410
+ )
411
+ expect(instanceResult.advancedSettingsErrors).toHaveLength(
412
+ staticResult.advancedSettingsErrors.length
413
+ )
414
+
415
+ // Compare error types
416
+ expect(instanceResult.baseErrors.map((e) => e.type)).toEqual(
417
+ staticResult.baseErrors.map((e) => e.type)
418
+ )
419
+ expect(
420
+ instanceResult.advancedSettingsErrors.map((e) => e.type)
421
+ ).toEqual(staticResult.advancedSettingsErrors.map((e) => e.type))
422
+
423
+ // Compare rendered templates
424
+ expect(
425
+ instanceResult.baseErrors.map((e) =>
426
+ typeof e.template === 'object' && 'rendered' in e.template
427
+ ? e.template.rendered
428
+ : e.template
429
+ )
430
+ ).toEqual(
431
+ staticResult.baseErrors.map((e) =>
432
+ typeof e.template === 'object' && 'rendered' in e.template
433
+ ? e.template.rendered
434
+ : e.template
435
+ )
436
+ )
408
437
  })
409
438
  })
410
439
  })
@@ -12,6 +12,7 @@ import {
12
12
  getLocationFieldViewModel
13
13
  } from '~/src/server/plugins/engine/components/LocationFieldHelpers.js'
14
14
  import { NumberField } from '~/src/server/plugins/engine/components/NumberField.js'
15
+ import { createLowerFirstExpression } from '~/src/server/plugins/engine/components/helpers/index.js'
15
16
  import { type LatLongState } from '~/src/server/plugins/engine/components/types.js'
16
17
  import { messageTemplate } from '~/src/server/plugins/engine/pageControllers/validationOptions.js'
17
18
  import {
@@ -194,29 +195,41 @@ export class LatLongField extends FormComponent {
194
195
  { type: 'required', template: messageTemplate.required },
195
196
  {
196
197
  type: 'latitudeFormat',
197
- template: 'Enter a valid latitude for {{#title}} like 51.519450'
198
+ template: createLowerFirstExpression(
199
+ 'Enter a valid latitude for {{lowerFirst(#title)}} like 51.519450'
200
+ )
198
201
  },
199
202
  {
200
203
  type: 'longitudeFormat',
201
- template: 'Enter a valid longitude for {{#title}} like -0.127758'
204
+ template: createLowerFirstExpression(
205
+ 'Enter a valid longitude for {{lowerFirst(#title)}} like -0.127758'
206
+ )
202
207
  }
203
208
  ],
204
209
  advancedSettingsErrors: [
205
210
  {
206
211
  type: 'latitudeMin',
207
- template: 'Latitude for {{#title}} must be between 49 and 60'
212
+ template: createLowerFirstExpression(
213
+ 'Latitude for {{lowerFirst(#title)}} must be between 49 and 60'
214
+ )
208
215
  },
209
216
  {
210
217
  type: 'latitudeMax',
211
- template: 'Latitude for {{#title}} must be between 49 and 60'
218
+ template: createLowerFirstExpression(
219
+ 'Latitude for {{lowerFirst(#title)}} must be between 49 and 60'
220
+ )
212
221
  },
213
222
  {
214
223
  type: 'longitudeMin',
215
- template: 'Longitude for {{#title}} must be between -9 and 2'
224
+ template: createLowerFirstExpression(
225
+ 'Longitude for {{lowerFirst(#title)}} must be between -9 and 2'
226
+ )
216
227
  },
217
228
  {
218
229
  type: 'longitudeMax',
219
- template: 'Longitude for {{#title}} must be between -9 and 2'
230
+ template: createLowerFirstExpression(
231
+ 'Longitude for {{lowerFirst(#title)}} must be between -9 and 2'
232
+ )
220
233
  }
221
234
  ]
222
235
  }
@@ -1,5 +1,9 @@
1
1
  import { type FormComponentsDef } from '@defra/forms-model'
2
- import joi, { type LanguageMessages, type StringSchema } from 'joi'
2
+ import joi, {
3
+ type JoiExpression,
4
+ type LanguageMessages,
5
+ type StringSchema
6
+ } from 'joi'
3
7
 
4
8
  import {
5
9
  FormComponent,
@@ -15,6 +19,7 @@ import {
15
19
  type FormSubmissionError,
16
20
  type FormSubmissionState
17
21
  } from '~/src/server/plugins/engine/types.js'
22
+ import { convertToLanguageMessages } from '~/src/server/utils/type-utils.js'
18
23
 
19
24
  interface LocationFieldOptions {
20
25
  instructionText?: string
@@ -26,8 +31,8 @@ interface LocationFieldOptions {
26
31
 
27
32
  interface ValidationConfig {
28
33
  pattern: RegExp
29
- patternErrorMessage: string
30
- requiredMessage?: string
34
+ patternErrorMessage: JoiExpression
35
+ requiredMessage?: JoiExpression
31
36
  }
32
37
 
33
38
  /**
@@ -42,7 +47,7 @@ export abstract class LocationFieldBase extends FormComponent {
42
47
  protected abstract getValidationConfig(): ValidationConfig
43
48
  protected abstract getErrorTemplates(): {
44
49
  type: string
45
- template: string
50
+ template: JoiExpression
46
51
  }[]
47
52
 
48
53
  constructor(
@@ -61,11 +66,11 @@ export abstract class LocationFieldBase extends FormComponent {
61
66
  const requiredMessage =
62
67
  config.requiredMessage ?? (messageTemplate.required as string)
63
68
 
64
- const messages: LanguageMessages = {
69
+ const messages = convertToLanguageMessages({
65
70
  'any.required': requiredMessage,
66
71
  'string.empty': requiredMessage,
67
72
  'string.pattern.base': config.patternErrorMessage
68
- }
73
+ })
69
74
 
70
75
  let formSchema = joi
71
76
  .string()
@@ -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
  }
@@ -129,7 +129,7 @@ describe('NationalGridFieldNumberField', () => {
129
129
 
130
130
  expect(result.errors).toEqual([
131
131
  expect.objectContaining({
132
- text: 'Enter Example National Grid field number'
132
+ text: 'Enter example National Grid field number'
133
133
  })
134
134
  ])
135
135
  })
@@ -293,7 +293,7 @@ describe('NationalGridFieldNumberField', () => {
293
293
  value: getFormData('NG1234567'),
294
294
  errors: expect.arrayContaining([
295
295
  expect.objectContaining({
296
- text: 'Enter a valid National Grid field number for Grid field like NG 1234 5678'
296
+ text: 'Enter a valid National Grid field number for grid field like NG 1234 5678'
297
297
  })
298
298
  ])
299
299
  }
@@ -304,7 +304,7 @@ describe('NationalGridFieldNumberField', () => {
304
304
  value: getFormData('N123456789'),
305
305
  errors: expect.arrayContaining([
306
306
  expect.objectContaining({
307
- text: 'Enter a valid National Grid field number for Grid field like NG 1234 5678'
307
+ text: 'Enter a valid National Grid field number for grid field like NG 1234 5678'
308
308
  })
309
309
  ])
310
310
  }
@@ -315,7 +315,7 @@ describe('NationalGridFieldNumberField', () => {
315
315
  value: getFormData('NGABCDEFGH'),
316
316
  errors: expect.arrayContaining([
317
317
  expect.objectContaining({
318
- text: 'Enter a valid National Grid field number for Grid field like NG 1234 5678'
318
+ text: 'Enter a valid National Grid field number for grid field like NG 1234 5678'
319
319
  })
320
320
  ])
321
321
  }
@@ -1,6 +1,7 @@
1
1
  import { type NationalGridFieldNumberFieldComponent } from '@defra/forms-model'
2
2
 
3
3
  import { LocationFieldBase } from '~/src/server/plugins/engine/components/LocationFieldBase.js'
4
+ import { createLowerFirstExpression } from '~/src/server/plugins/engine/components/helpers/index.js'
4
5
 
5
6
  export class NationalGridFieldNumberField extends LocationFieldBase {
6
7
  declare options: NationalGridFieldNumberFieldComponent['options']
@@ -12,10 +13,15 @@ export class NationalGridFieldNumberField extends LocationFieldBase {
12
13
  const pattern =
13
14
  /^((([sS]|[nN])[a-hA-Hj-zJ-Z])|(([tT]|[oO])[abfglmqrvwABFGLMQRVW])|([hH][l-zL-Z])|([jJ][lmqrvwLMQRVW]))\s?([0-9]{4})\s?([0-9]{4})$/
14
15
 
16
+ const patternTemplate =
17
+ 'Enter a valid National Grid field number for {{lowerFirst(#title)}} like NG 1234 5678'
18
+
15
19
  return {
16
20
  pattern,
17
- patternErrorMessage: `Enter a valid National Grid field number for {{#title}} like NG 1234 5678`,
18
- requiredMessage: 'Enter {{#title}}'
21
+ patternErrorMessage: createLowerFirstExpression(patternTemplate),
22
+ requiredMessage: createLowerFirstExpression(
23
+ 'Enter {{lowerFirst(#title)}}'
24
+ )
19
25
  }
20
26
  }
21
27
 
@@ -23,8 +29,9 @@ export class NationalGridFieldNumberField extends LocationFieldBase {
23
29
  return [
24
30
  {
25
31
  type: 'pattern',
26
- template:
27
- 'Enter a valid National Grid field number for {{#title}} like NG 1234 5678'
32
+ template: createLowerFirstExpression(
33
+ 'Enter a valid National Grid field number for {{lowerFirst(#title)}} like NG 1234 5678'
34
+ )
28
35
  }
29
36
  ]
30
37
  }
@@ -135,7 +135,7 @@ describe('OsGridRefField', () => {
135
135
 
136
136
  expect(result.errors).toEqual([
137
137
  expect.objectContaining({
138
- text: 'Enter Example OS grid reference'
138
+ text: 'Enter example OS grid reference'
139
139
  })
140
140
  ])
141
141
  })
@@ -308,7 +308,7 @@ describe('OsGridRefField', () => {
308
308
  value: getFormData('TQ12345'),
309
309
  errors: expect.arrayContaining([
310
310
  expect.objectContaining({
311
- text: 'Enter a valid OS grid reference for Grid reference like TQ123456'
311
+ text: 'Enter a valid OS grid reference for grid reference like TQ123456'
312
312
  })
313
313
  ])
314
314
  }
@@ -319,7 +319,7 @@ describe('OsGridRefField', () => {
319
319
  value: getFormData('AA1234567'),
320
320
  errors: expect.arrayContaining([
321
321
  expect.objectContaining({
322
- text: 'Enter a valid OS grid reference for Grid reference like TQ123456'
322
+ text: 'Enter a valid OS grid reference for grid reference like TQ123456'
323
323
  })
324
324
  ])
325
325
  }
@@ -330,7 +330,7 @@ describe('OsGridRefField', () => {
330
330
  value: getFormData('TQABCDEF'),
331
331
  errors: expect.arrayContaining([
332
332
  expect.objectContaining({
333
- text: 'Enter a valid OS grid reference for Grid reference like TQ123456'
333
+ text: 'Enter a valid OS grid reference for grid reference like TQ123456'
334
334
  })
335
335
  ])
336
336
  }