@defra/forms-engine-plugin 4.0.36 → 4.0.37

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.
@@ -10,6 +10,11 @@ export declare const addClassOptionIfNone: (options: Extract<ComponentDef, {
10
10
  classes?: string;
11
11
  };
12
12
  }>["options"], className: string) => void;
13
+ /**
14
+ * Applies lowerFirst but preserves capitalisation of proper nouns
15
+ * like "National Grid", "Ordnance Survey" and "OS".
16
+ */
17
+ export declare function lowerFirstPreserveProperNouns(text: string): string;
13
18
  /**
14
19
  * Configuration for Joi expressions that use lowerFirst function
15
20
  */
@@ -16,12 +16,21 @@ export const addClassOptionIfNone = (options, className) => {
16
16
  options.classes ??= className;
17
17
  };
18
18
 
19
+ /**
20
+ * Applies lowerFirst but preserves capitalisation of proper nouns
21
+ * like "National Grid", "Ordnance Survey" and "OS".
22
+ */
23
+ export function lowerFirstPreserveProperNouns(text) {
24
+ const result = lowerFirst(text);
25
+ return result.replace(/\bnational [Gg]rid\b/g, 'National Grid').replace(/\bordnance [Ss]urvey\b/g, 'Ordnance Survey').replace(/\b[oO][sS]\b/g, 'OS');
26
+ }
27
+
19
28
  /**
20
29
  * Configuration for Joi expressions that use lowerFirst function
21
30
  */
22
31
  export const lowerFirstExpressionOptions = {
23
32
  functions: {
24
- lowerFirst
33
+ lowerFirst: lowerFirstPreserveProperNouns
25
34
  }
26
35
  };
27
36
 
@@ -1 +1 @@
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":[]}
1
+ {"version":3,"file":"index.js","names":["joi","lowerFirst","escapeMarkdown","answer","punctuation","character","replaceAll","addClassOptionIfNone","options","className","classes","lowerFirstPreserveProperNouns","text","result","replace","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 * Applies lowerFirst but preserves capitalisation of proper nouns\n * like \"National Grid\", \"Ordnance Survey\" and \"OS\".\n */\nexport function lowerFirstPreserveProperNouns(text: string): string {\n const result = lowerFirst(text)\n return result\n .replace(/\\bnational [Gg]rid\\b/g, 'National Grid')\n .replace(/\\bordnance [Ss]urvey\\b/g, 'Ordnance Survey')\n .replace(/\\b[oO][sS]\\b/g, 'OS')\n}\n\n/**\n * Configuration for Joi expressions that use lowerFirst function\n */\nexport const lowerFirstExpressionOptions = {\n functions: {\n lowerFirst: lowerFirstPreserveProperNouns\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;AACA,OAAO,SAASE,6BAA6BA,CAACC,IAAY,EAAU;EAClE,MAAMC,MAAM,GAAGZ,UAAU,CAACW,IAAI,CAAC;EAC/B,OAAOC,MAAM,CACVC,OAAO,CAAC,uBAAuB,EAAE,eAAe,CAAC,CACjDA,OAAO,CAAC,yBAAyB,EAAE,iBAAiB,CAAC,CACrDA,OAAO,CAAC,eAAe,EAAE,IAAI,CAAC;AACnC;;AAEA;AACA;AACA;AACA,OAAO,MAAMC,2BAA2B,GAAG;EACzCC,SAAS,EAAE;IACTf,UAAU,EAAEU;EACd;AACF,CAAqB;;AAErB;AACA;AACA;AACA;AACA,OAAO,MAAMM,0BAA0B,GAAIC,QAAgB,IACzDlB,GAAG,CAACmB,UAAU,CAACD,QAAQ,EAAEH,2BAA2B,CAAkB","ignoreList":[]}
@@ -2,10 +2,10 @@
2
2
  // Declaration above is needed for: https://github.com/hapijs/joi/issues/3064
3
3
 
4
4
  import joi from 'joi';
5
- import lowerFirst from 'lodash/lowerFirst.js';
5
+ import { lowerFirstPreserveProperNouns } from "../components/helpers/index.js";
6
6
  const opts = {
7
7
  functions: {
8
- lowerFirst
8
+ lowerFirst: lowerFirstPreserveProperNouns
9
9
  }
10
10
  };
11
11
 
@@ -1 +1 @@
1
- {"version":3,"file":"validationOptions.js","names":["joi","lowerFirst","opts","functions","messageTemplate","declarationRequired","expression","required","selectRequired","selectYesNoRequired","max","min","minMax","pattern","format","number","numberPrecision","numberInteger","numberMin","numberMax","maxWords","objectRequired","objectMissing","dateFormat","dateMin","dateMax","messages","messagesPre","validationOptions","abortEarly","errors","wrap","array","label"],"sources":["../../../../../src/server/plugins/engine/pageControllers/validationOptions.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-unsafe-assignment */\n// Declaration above is needed for: https://github.com/hapijs/joi/issues/3064\n\nimport joi, {\n type JoiExpression,\n type LanguageMessages,\n type LanguageMessagesExt,\n type ReferenceOptions,\n type ValidationOptions\n} from 'joi'\nimport lowerFirst from 'lodash/lowerFirst.js'\n\nconst opts = {\n functions: {\n lowerFirst\n }\n} as ReferenceOptions\n\n/**\n * see @link https://joi.dev/api/?v=17.4.2#template-syntax for template syntax\n */\nexport const messageTemplate: Record<string, JoiExpression> = {\n declarationRequired: joi.expression(\n 'You must confirm you understand and agree with the {{lowerFirst(#label)}} to continue',\n opts\n ) as JoiExpression,\n required: joi.expression(\n 'Enter {{lowerFirst(#label)}}',\n opts\n ) as JoiExpression,\n selectRequired: joi.expression(\n 'Select {{lowerFirst(#label)}}',\n opts\n ) as JoiExpression,\n selectYesNoRequired: '{{#label}} - select yes or no',\n max: '{{#label}} must be {{#limit}} characters or less',\n min: '{{#label}} must be {{#limit}} characters or more',\n minMax: '{{#label}} must be between {{#min}} and {{#max}} characters',\n pattern: joi.expression(\n 'Enter a valid {{lowerFirst(#label)}}',\n opts\n ) as JoiExpression,\n format: joi.expression(\n 'Enter {{lowerFirst(#label)}} in the correct format',\n opts\n ) as JoiExpression,\n number: '{{#label}} must be a number',\n numberPrecision: '{{#label}} must have {{#limit}} or fewer decimal places',\n numberInteger: '{{#label}} must be a whole number',\n numberMin: '{{#label}} must be {{#limit}} or higher',\n numberMax: '{{#label}} must be {{#limit}} or lower',\n maxWords: '{{#label}} must be {{#limit}} words or fewer',\n\n // Nested fields use component title\n\n objectRequired: joi.expression('Enter {{#label}}', opts) as JoiExpression,\n objectMissing: joi.expression(\n '{{#title}} must include a {{lowerFirst(#label)}}',\n opts\n ) as JoiExpression,\n dateFormat: '{{#title}} must be a real date',\n dateMin: '{{#title}} must be the same as or after {{#limit}}',\n dateMax: '{{#title}} must be the same as or before {{#limit}}'\n}\n\nexport const messages: LanguageMessagesExt = {\n 'string.base': messageTemplate.required,\n 'string.min': messageTemplate.min,\n 'string.empty': messageTemplate.required,\n 'string.max': messageTemplate.max,\n 'string.email': messageTemplate.format,\n 'string.pattern.base': messageTemplate.pattern,\n 'string.maxWords': messageTemplate.maxWords,\n\n 'number.base': messageTemplate.number,\n 'number.precision': messageTemplate.numberPrecision,\n 'number.integer': messageTemplate.numberInteger,\n 'number.unsafe': messageTemplate.format,\n 'number.min': messageTemplate.numberMin,\n 'number.max': messageTemplate.numberMax,\n\n 'object.required': messageTemplate.objectRequired,\n 'object.and': messageTemplate.objectMissing,\n\n 'any.only': messageTemplate.selectRequired,\n 'any.required': messageTemplate.selectRequired,\n 'any.empty': messageTemplate.required,\n\n 'date.base': messageTemplate.dateFormat,\n 'date.format': messageTemplate.dateFormat,\n 'date.min': messageTemplate.dateMin,\n 'date.max': messageTemplate.dateMax\n}\n\nexport const messagesPre: LanguageMessages =\n messages as unknown as LanguageMessages\n\nexport const validationOptions: ValidationOptions = {\n abortEarly: false,\n messages: messagesPre,\n errors: {\n wrap: {\n array: false,\n label: false\n }\n }\n}\n"],"mappings":"AAAA;AACA;;AAEA,OAAOA,GAAG,MAMH,KAAK;AACZ,OAAOC,UAAU,MAAM,sBAAsB;AAE7C,MAAMC,IAAI,GAAG;EACXC,SAAS,EAAE;IACTF;EACF;AACF,CAAqB;;AAErB;AACA;AACA;AACA,OAAO,MAAMG,eAA8C,GAAG;EAC5DC,mBAAmB,EAAEL,GAAG,CAACM,UAAU,CACjC,uFAAuF,EACvFJ,IACF,CAAkB;EAClBK,QAAQ,EAAEP,GAAG,CAACM,UAAU,CACtB,8BAA8B,EAC9BJ,IACF,CAAkB;EAClBM,cAAc,EAAER,GAAG,CAACM,UAAU,CAC5B,+BAA+B,EAC/BJ,IACF,CAAkB;EAClBO,mBAAmB,EAAE,+BAA+B;EACpDC,GAAG,EAAE,kDAAkD;EACvDC,GAAG,EAAE,kDAAkD;EACvDC,MAAM,EAAE,6DAA6D;EACrEC,OAAO,EAAEb,GAAG,CAACM,UAAU,CACrB,sCAAsC,EACtCJ,IACF,CAAkB;EAClBY,MAAM,EAAEd,GAAG,CAACM,UAAU,CACpB,oDAAoD,EACpDJ,IACF,CAAkB;EAClBa,MAAM,EAAE,6BAA6B;EACrCC,eAAe,EAAE,yDAAyD;EAC1EC,aAAa,EAAE,mCAAmC;EAClDC,SAAS,EAAE,yCAAyC;EACpDC,SAAS,EAAE,wCAAwC;EACnDC,QAAQ,EAAE,8CAA8C;EAExD;;EAEAC,cAAc,EAAErB,GAAG,CAACM,UAAU,CAAC,kBAAkB,EAAEJ,IAAI,CAAkB;EACzEoB,aAAa,EAAEtB,GAAG,CAACM,UAAU,CAC3B,kDAAkD,EAClDJ,IACF,CAAkB;EAClBqB,UAAU,EAAE,gCAAgC;EAC5CC,OAAO,EAAE,oDAAoD;EAC7DC,OAAO,EAAE;AACX,CAAC;AAED,OAAO,MAAMC,QAA6B,GAAG;EAC3C,aAAa,EAAEtB,eAAe,CAACG,QAAQ;EACvC,YAAY,EAAEH,eAAe,CAACO,GAAG;EACjC,cAAc,EAAEP,eAAe,CAACG,QAAQ;EACxC,YAAY,EAAEH,eAAe,CAACM,GAAG;EACjC,cAAc,EAAEN,eAAe,CAACU,MAAM;EACtC,qBAAqB,EAAEV,eAAe,CAACS,OAAO;EAC9C,iBAAiB,EAAET,eAAe,CAACgB,QAAQ;EAE3C,aAAa,EAAEhB,eAAe,CAACW,MAAM;EACrC,kBAAkB,EAAEX,eAAe,CAACY,eAAe;EACnD,gBAAgB,EAAEZ,eAAe,CAACa,aAAa;EAC/C,eAAe,EAAEb,eAAe,CAACU,MAAM;EACvC,YAAY,EAAEV,eAAe,CAACc,SAAS;EACvC,YAAY,EAAEd,eAAe,CAACe,SAAS;EAEvC,iBAAiB,EAAEf,eAAe,CAACiB,cAAc;EACjD,YAAY,EAAEjB,eAAe,CAACkB,aAAa;EAE3C,UAAU,EAAElB,eAAe,CAACI,cAAc;EAC1C,cAAc,EAAEJ,eAAe,CAACI,cAAc;EAC9C,WAAW,EAAEJ,eAAe,CAACG,QAAQ;EAErC,WAAW,EAAEH,eAAe,CAACmB,UAAU;EACvC,aAAa,EAAEnB,eAAe,CAACmB,UAAU;EACzC,UAAU,EAAEnB,eAAe,CAACoB,OAAO;EACnC,UAAU,EAAEpB,eAAe,CAACqB;AAC9B,CAAC;AAED,OAAO,MAAME,WAA6B,GACxCD,QAAuC;AAEzC,OAAO,MAAME,iBAAoC,GAAG;EAClDC,UAAU,EAAE,KAAK;EACjBH,QAAQ,EAAEC,WAAW;EACrBG,MAAM,EAAE;IACNC,IAAI,EAAE;MACJC,KAAK,EAAE,KAAK;MACZC,KAAK,EAAE;IACT;EACF;AACF,CAAC","ignoreList":[]}
1
+ {"version":3,"file":"validationOptions.js","names":["joi","lowerFirstPreserveProperNouns","opts","functions","lowerFirst","messageTemplate","declarationRequired","expression","required","selectRequired","selectYesNoRequired","max","min","minMax","pattern","format","number","numberPrecision","numberInteger","numberMin","numberMax","maxWords","objectRequired","objectMissing","dateFormat","dateMin","dateMax","messages","messagesPre","validationOptions","abortEarly","errors","wrap","array","label"],"sources":["../../../../../src/server/plugins/engine/pageControllers/validationOptions.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-unsafe-assignment */\n// Declaration above is needed for: https://github.com/hapijs/joi/issues/3064\n\nimport joi, {\n type JoiExpression,\n type LanguageMessages,\n type LanguageMessagesExt,\n type ReferenceOptions,\n type ValidationOptions\n} from 'joi'\n\nimport { lowerFirstPreserveProperNouns } from '~/src/server/plugins/engine/components/helpers/index.js'\n\nconst opts = {\n functions: {\n lowerFirst: lowerFirstPreserveProperNouns\n }\n} as ReferenceOptions\n\n/**\n * see @link https://joi.dev/api/?v=17.4.2#template-syntax for template syntax\n */\nexport const messageTemplate: Record<string, JoiExpression> = {\n declarationRequired: joi.expression(\n 'You must confirm you understand and agree with the {{lowerFirst(#label)}} to continue',\n opts\n ) as JoiExpression,\n required: joi.expression(\n 'Enter {{lowerFirst(#label)}}',\n opts\n ) as JoiExpression,\n selectRequired: joi.expression(\n 'Select {{lowerFirst(#label)}}',\n opts\n ) as JoiExpression,\n selectYesNoRequired: '{{#label}} - select yes or no',\n max: '{{#label}} must be {{#limit}} characters or less',\n min: '{{#label}} must be {{#limit}} characters or more',\n minMax: '{{#label}} must be between {{#min}} and {{#max}} characters',\n pattern: joi.expression(\n 'Enter a valid {{lowerFirst(#label)}}',\n opts\n ) as JoiExpression,\n format: joi.expression(\n 'Enter {{lowerFirst(#label)}} in the correct format',\n opts\n ) as JoiExpression,\n number: '{{#label}} must be a number',\n numberPrecision: '{{#label}} must have {{#limit}} or fewer decimal places',\n numberInteger: '{{#label}} must be a whole number',\n numberMin: '{{#label}} must be {{#limit}} or higher',\n numberMax: '{{#label}} must be {{#limit}} or lower',\n maxWords: '{{#label}} must be {{#limit}} words or fewer',\n\n // Nested fields use component title\n\n objectRequired: joi.expression('Enter {{#label}}', opts) as JoiExpression,\n objectMissing: joi.expression(\n '{{#title}} must include a {{lowerFirst(#label)}}',\n opts\n ) as JoiExpression,\n dateFormat: '{{#title}} must be a real date',\n dateMin: '{{#title}} must be the same as or after {{#limit}}',\n dateMax: '{{#title}} must be the same as or before {{#limit}}'\n}\n\nexport const messages: LanguageMessagesExt = {\n 'string.base': messageTemplate.required,\n 'string.min': messageTemplate.min,\n 'string.empty': messageTemplate.required,\n 'string.max': messageTemplate.max,\n 'string.email': messageTemplate.format,\n 'string.pattern.base': messageTemplate.pattern,\n 'string.maxWords': messageTemplate.maxWords,\n\n 'number.base': messageTemplate.number,\n 'number.precision': messageTemplate.numberPrecision,\n 'number.integer': messageTemplate.numberInteger,\n 'number.unsafe': messageTemplate.format,\n 'number.min': messageTemplate.numberMin,\n 'number.max': messageTemplate.numberMax,\n\n 'object.required': messageTemplate.objectRequired,\n 'object.and': messageTemplate.objectMissing,\n\n 'any.only': messageTemplate.selectRequired,\n 'any.required': messageTemplate.selectRequired,\n 'any.empty': messageTemplate.required,\n\n 'date.base': messageTemplate.dateFormat,\n 'date.format': messageTemplate.dateFormat,\n 'date.min': messageTemplate.dateMin,\n 'date.max': messageTemplate.dateMax\n}\n\nexport const messagesPre: LanguageMessages =\n messages as unknown as LanguageMessages\n\nexport const validationOptions: ValidationOptions = {\n abortEarly: false,\n messages: messagesPre,\n errors: {\n wrap: {\n array: false,\n label: false\n }\n }\n}\n"],"mappings":"AAAA;AACA;;AAEA,OAAOA,GAAG,MAMH,KAAK;AAEZ,SAASC,6BAA6B;AAEtC,MAAMC,IAAI,GAAG;EACXC,SAAS,EAAE;IACTC,UAAU,EAAEH;EACd;AACF,CAAqB;;AAErB;AACA;AACA;AACA,OAAO,MAAMI,eAA8C,GAAG;EAC5DC,mBAAmB,EAAEN,GAAG,CAACO,UAAU,CACjC,uFAAuF,EACvFL,IACF,CAAkB;EAClBM,QAAQ,EAAER,GAAG,CAACO,UAAU,CACtB,8BAA8B,EAC9BL,IACF,CAAkB;EAClBO,cAAc,EAAET,GAAG,CAACO,UAAU,CAC5B,+BAA+B,EAC/BL,IACF,CAAkB;EAClBQ,mBAAmB,EAAE,+BAA+B;EACpDC,GAAG,EAAE,kDAAkD;EACvDC,GAAG,EAAE,kDAAkD;EACvDC,MAAM,EAAE,6DAA6D;EACrEC,OAAO,EAAEd,GAAG,CAACO,UAAU,CACrB,sCAAsC,EACtCL,IACF,CAAkB;EAClBa,MAAM,EAAEf,GAAG,CAACO,UAAU,CACpB,oDAAoD,EACpDL,IACF,CAAkB;EAClBc,MAAM,EAAE,6BAA6B;EACrCC,eAAe,EAAE,yDAAyD;EAC1EC,aAAa,EAAE,mCAAmC;EAClDC,SAAS,EAAE,yCAAyC;EACpDC,SAAS,EAAE,wCAAwC;EACnDC,QAAQ,EAAE,8CAA8C;EAExD;;EAEAC,cAAc,EAAEtB,GAAG,CAACO,UAAU,CAAC,kBAAkB,EAAEL,IAAI,CAAkB;EACzEqB,aAAa,EAAEvB,GAAG,CAACO,UAAU,CAC3B,kDAAkD,EAClDL,IACF,CAAkB;EAClBsB,UAAU,EAAE,gCAAgC;EAC5CC,OAAO,EAAE,oDAAoD;EAC7DC,OAAO,EAAE;AACX,CAAC;AAED,OAAO,MAAMC,QAA6B,GAAG;EAC3C,aAAa,EAAEtB,eAAe,CAACG,QAAQ;EACvC,YAAY,EAAEH,eAAe,CAACO,GAAG;EACjC,cAAc,EAAEP,eAAe,CAACG,QAAQ;EACxC,YAAY,EAAEH,eAAe,CAACM,GAAG;EACjC,cAAc,EAAEN,eAAe,CAACU,MAAM;EACtC,qBAAqB,EAAEV,eAAe,CAACS,OAAO;EAC9C,iBAAiB,EAAET,eAAe,CAACgB,QAAQ;EAE3C,aAAa,EAAEhB,eAAe,CAACW,MAAM;EACrC,kBAAkB,EAAEX,eAAe,CAACY,eAAe;EACnD,gBAAgB,EAAEZ,eAAe,CAACa,aAAa;EAC/C,eAAe,EAAEb,eAAe,CAACU,MAAM;EACvC,YAAY,EAAEV,eAAe,CAACc,SAAS;EACvC,YAAY,EAAEd,eAAe,CAACe,SAAS;EAEvC,iBAAiB,EAAEf,eAAe,CAACiB,cAAc;EACjD,YAAY,EAAEjB,eAAe,CAACkB,aAAa;EAE3C,UAAU,EAAElB,eAAe,CAACI,cAAc;EAC1C,cAAc,EAAEJ,eAAe,CAACI,cAAc;EAC9C,WAAW,EAAEJ,eAAe,CAACG,QAAQ;EAErC,WAAW,EAAEH,eAAe,CAACmB,UAAU;EACvC,aAAa,EAAEnB,eAAe,CAACmB,UAAU;EACzC,UAAU,EAAEnB,eAAe,CAACoB,OAAO;EACnC,UAAU,EAAEpB,eAAe,CAACqB;AAC9B,CAAC;AAED,OAAO,MAAME,WAA6B,GACxCD,QAAuC;AAEzC,OAAO,MAAME,iBAAoC,GAAG;EAClDC,UAAU,EAAE,KAAK;EACjBH,QAAQ,EAAEC,WAAW;EACrBG,MAAM,EAAE;IACNC,IAAI,EAAE;MACJC,KAAK,EAAE,KAAK;MACZC,KAAK,EAAE;IACT;EACF;AACF,CAAC","ignoreList":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@defra/forms-engine-plugin",
3
- "version": "4.0.36",
3
+ "version": "4.0.37",
4
4
  "description": "Defra forms engine",
5
5
  "type": "module",
6
6
  "files": [
@@ -9,7 +9,8 @@ import { OsGridRefField } from '~/src/server/plugins/engine/components/OsGridRef
9
9
  import { createComponent } from '~/src/server/plugins/engine/components/helpers/components.js'
10
10
  import {
11
11
  createLowerFirstExpression,
12
- lowerFirstExpressionOptions
12
+ lowerFirstExpressionOptions,
13
+ lowerFirstPreserveProperNouns
13
14
  } from '~/src/server/plugins/engine/components/helpers/index.js'
14
15
  import { FormModel } from '~/src/server/plugins/engine/models/FormModel.js'
15
16
  import definition from '~/test/form/definitions/basic.js'
@@ -145,6 +146,42 @@ describe('ComponentBase tests', () => {
145
146
  })
146
147
  })
147
148
 
149
+ describe('lowerFirstPreserveProperNouns', () => {
150
+ test('should preserve "National Grid" capitalisation', () => {
151
+ expect(lowerFirstPreserveProperNouns('National Grid field number')).toBe(
152
+ 'National Grid field number'
153
+ )
154
+ })
155
+
156
+ test('should preserve "Ordnance Survey" capitalisation', () => {
157
+ expect(
158
+ lowerFirstPreserveProperNouns('Ordnance Survey (OS) grid reference')
159
+ ).toBe('Ordnance Survey (OS) grid reference')
160
+ })
161
+
162
+ test('should preserve "OS" capitalisation', () => {
163
+ expect(lowerFirstPreserveProperNouns('OS grid reference')).toBe(
164
+ 'OS grid reference'
165
+ )
166
+ })
167
+
168
+ test('should lowercase first character for regular text', () => {
169
+ expect(lowerFirstPreserveProperNouns('Enter your name')).toBe(
170
+ 'enter your name'
171
+ )
172
+ })
173
+
174
+ test('should handle text without special terms', () => {
175
+ expect(lowerFirstPreserveProperNouns('Latitude and longitude')).toBe(
176
+ 'latitude and longitude'
177
+ )
178
+ })
179
+
180
+ test('should handle empty string', () => {
181
+ expect(lowerFirstPreserveProperNouns('')).toBe('')
182
+ })
183
+ })
184
+
148
185
  describe('lowerFirst expression helpers', () => {
149
186
  test('lowerFirstExpressionOptions should have lowerFirst function', () => {
150
187
  expect(lowerFirstExpressionOptions).toHaveProperty('functions')
@@ -39,12 +39,24 @@ export const addClassOptionIfNone = (
39
39
  options.classes ??= className
40
40
  }
41
41
 
42
+ /**
43
+ * Applies lowerFirst but preserves capitalisation of proper nouns
44
+ * like "National Grid", "Ordnance Survey" and "OS".
45
+ */
46
+ export function lowerFirstPreserveProperNouns(text: string): string {
47
+ const result = lowerFirst(text)
48
+ return result
49
+ .replace(/\bnational [Gg]rid\b/g, 'National Grid')
50
+ .replace(/\bordnance [Ss]urvey\b/g, 'Ordnance Survey')
51
+ .replace(/\b[oO][sS]\b/g, 'OS')
52
+ }
53
+
42
54
  /**
43
55
  * Configuration for Joi expressions that use lowerFirst function
44
56
  */
45
57
  export const lowerFirstExpressionOptions = {
46
58
  functions: {
47
- lowerFirst
59
+ lowerFirst: lowerFirstPreserveProperNouns
48
60
  }
49
61
  } as ReferenceOptions
50
62
 
@@ -8,11 +8,12 @@ import joi, {
8
8
  type ReferenceOptions,
9
9
  type ValidationOptions
10
10
  } from 'joi'
11
- import lowerFirst from 'lodash/lowerFirst.js'
11
+
12
+ import { lowerFirstPreserveProperNouns } from '~/src/server/plugins/engine/components/helpers/index.js'
12
13
 
13
14
  const opts = {
14
15
  functions: {
15
- lowerFirst
16
+ lowerFirst: lowerFirstPreserveProperNouns
16
17
  }
17
18
  } as ReferenceOptions
18
19