@defra/forms-engine-plugin 2.1.9 → 2.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.server/server/plugins/engine/components/helpers/components.js +1 -1
- package/.server/server/plugins/engine/components/helpers/components.js.map +1 -1
- package/package.json +1 -1
- package/src/server/plugins/engine/components/MultilineTextField.test.ts +11 -0
- package/src/server/plugins/engine/components/helpers/components.ts +1 -1
|
@@ -210,7 +210,7 @@ export function getAnswerMarkdown(field, state, options = {
|
|
|
210
210
|
}).join('');
|
|
211
211
|
} else if (field instanceof Components.MultilineTextField) {
|
|
212
212
|
// Preserve Multiline text new lines
|
|
213
|
-
answerEscaped = answer.split(
|
|
213
|
+
answerEscaped = answer.split(/(?:\r?\n)+/).map(escapeMarkdown).join('\n').concat('\n');
|
|
214
214
|
} else if (field instanceof Components.UkAddressField) {
|
|
215
215
|
// Format UK addresses into new lines
|
|
216
216
|
answerEscaped = (field.getContextValueFromState(state) ?? []).map(escapeMarkdown).join('\n').concat('\n');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"components.js","names":["ComponentType","Marked","config","ListFormComponent","escapeMarkdown","Components","designerUrl","get","markdown","breaks","gfm","extensions","name","renderer","tokens","text","parser","parseInline","length","walkTokens","token","includes","type","hasListFormField","field","isListFieldType","allowedTypes","AutocompleteField","CheckboxesField","RadiosField","SelectField","YesNoField","createComponent","def","options","component","DatePartsField","Details","EmailAddressField","Html","InsetText","List","Markdown","MultilineTextField","NumberField","TelephoneNumberField","TextField","UkAddressField","MonthYearField","FileUploadField","Error","getAnswer","state","format","getAnswerMarkdown","context","getContextValueFromState","toString","parse","async","trim","getDisplayStringFromState","answer","answerEscaped","files","getFormValueFromState","map","status","file","form","filename","fileId","join","values","flat","items","filter","value","item","label","line","toLowerCase","split","concat"],"sources":["../../../../../../src/server/plugins/engine/components/helpers/components.ts"],"sourcesContent":["import { ComponentType, type ComponentDef } from '@defra/forms-model'\nimport { Marked, type Token } from 'marked'\n\nimport { config } from '~/src/config/index.js'\nimport { type ComponentBase } from '~/src/server/plugins/engine/components/ComponentBase.js'\nimport { ListFormComponent } from '~/src/server/plugins/engine/components/ListFormComponent.js'\nimport { escapeMarkdown } from '~/src/server/plugins/engine/components/helpers/index.js'\nimport * as Components from '~/src/server/plugins/engine/components/index.js'\nimport { type FormState } from '~/src/server/plugins/engine/types.js'\n\n// All component instances\nexport type Component = InstanceType<\n (typeof Components)[keyof typeof Components]\n>\n\n// Field component instances only\nexport type Field = InstanceType<\n | typeof Components.AutocompleteField\n | typeof Components.RadiosField\n | typeof Components.YesNoField\n | typeof Components.CheckboxesField\n | typeof Components.DatePartsField\n | typeof Components.EmailAddressField\n | typeof Components.MonthYearField\n | typeof Components.MultilineTextField\n | typeof Components.NumberField\n | typeof Components.SelectField\n | typeof Components.TelephoneNumberField\n | typeof Components.TextField\n | typeof Components.UkAddressField\n | typeof Components.FileUploadField\n>\n\n// Guidance component instances only\nexport type Guidance = InstanceType<\n | typeof Components.Details\n | typeof Components.Html\n | typeof Components.Markdown\n | typeof Components.InsetText\n | typeof Components.List\n>\n\n// List component instances only\nexport type ListField = InstanceType<\n | typeof Components.AutocompleteField\n | typeof Components.CheckboxesField\n | typeof Components.RadiosField\n | typeof Components.SelectField\n | typeof Components.YesNoField\n>\n\nexport const designerUrl = config.get('designerUrl')\n\nexport const markdown = new Marked({\n breaks: true,\n gfm: true,\n\n /**\n * Render paragraphs without `<p>` wrappers\n * for check answers summary list `<dd>`\n */\n extensions: [\n {\n name: 'paragraph',\n renderer({ tokens = [] }) {\n const text = this.parser.parseInline(tokens)\n return tokens.length > 1 ? `${text}<br>` : text\n }\n }\n ],\n\n /**\n * Restrict allowed Markdown tokens\n */\n walkTokens(token) {\n const tokens: Token['type'][] = [\n 'br',\n 'escape',\n 'list',\n 'list_item',\n 'paragraph',\n 'space',\n 'text'\n ]\n\n if (!tokens.includes(token.type)) {\n token.type = 'text'\n }\n }\n})\n\n/**\n * Filter known components with lists\n */\nexport function hasListFormField(\n field?: Partial<Component>\n): field is ListFormComponent {\n return !!field && isListFieldType(field.type)\n}\n\nexport function isListFieldType(\n type?: ComponentType\n): type is ListField['type'] {\n const allowedTypes = [\n ComponentType.AutocompleteField,\n ComponentType.CheckboxesField,\n ComponentType.RadiosField,\n ComponentType.SelectField,\n ComponentType.YesNoField\n ]\n\n return !!type && allowedTypes.includes(type)\n}\n\n/**\n * Create field instance for each {@link ComponentDef} type\n */\nexport function createComponent(\n def: ComponentDef,\n options: ConstructorParameters<typeof ComponentBase>[1]\n): Component {\n let component: Component | undefined\n\n switch (def.type) {\n case ComponentType.AutocompleteField:\n component = new Components.AutocompleteField(def, options)\n break\n\n case ComponentType.CheckboxesField:\n component = new Components.CheckboxesField(def, options)\n break\n\n case ComponentType.DatePartsField:\n component = new Components.DatePartsField(def, options)\n break\n\n case ComponentType.Details:\n component = new Components.Details(def, options)\n break\n\n case ComponentType.EmailAddressField:\n component = new Components.EmailAddressField(def, options)\n break\n\n case ComponentType.Html:\n component = new Components.Html(def, options)\n break\n\n case ComponentType.InsetText:\n component = new Components.InsetText(def, options)\n break\n\n case ComponentType.List:\n component = new Components.List(def, options)\n break\n\n case ComponentType.Markdown:\n component = new Components.Markdown(def, options)\n break\n\n case ComponentType.MultilineTextField:\n component = new Components.MultilineTextField(def, options)\n break\n\n case ComponentType.NumberField:\n component = new Components.NumberField(def, options)\n break\n\n case ComponentType.RadiosField:\n component = new Components.RadiosField(def, options)\n break\n\n case ComponentType.SelectField:\n component = new Components.SelectField(def, options)\n break\n\n case ComponentType.TelephoneNumberField:\n component = new Components.TelephoneNumberField(def, options)\n break\n\n case ComponentType.TextField:\n component = new Components.TextField(def, options)\n break\n\n case ComponentType.UkAddressField:\n component = new Components.UkAddressField(def, options)\n break\n\n case ComponentType.YesNoField:\n component = new Components.YesNoField(def, options)\n break\n\n case ComponentType.MonthYearField:\n component = new Components.MonthYearField(def, options)\n break\n\n case ComponentType.FileUploadField:\n component = new Components.FileUploadField(def, options)\n break\n }\n\n if (typeof component === 'undefined') {\n throw new Error(`Component type ${def.type} does not exist`)\n }\n\n return component\n}\n\n/**\n * Get formatted answer for a field\n */\nexport function getAnswer(\n field: Field,\n state: FormState,\n options: {\n format:\n | 'data' // Submission data\n | 'email' // GOV.UK Notify emails\n | 'summary' // Check answers summary\n } = { format: 'summary' }\n) {\n // Use escaped display text for GOV.UK Notify emails\n if (options.format === 'email') {\n return getAnswerMarkdown(field, state, { format: 'email' })\n }\n\n // Use context value for submission data\n if (options.format === 'data') {\n const context = field.getContextValueFromState(state)\n return context?.toString() ?? ''\n }\n\n // Use display HTML for check answers summary (multi line)\n if (\n field instanceof ListFormComponent ||\n field instanceof Components.MultilineTextField ||\n field instanceof Components.UkAddressField\n ) {\n return markdown\n .parse(getAnswerMarkdown(field, state), { async: false })\n .trim()\n }\n\n // Use display text for check answers summary (single line)\n return field.getDisplayStringFromState(state)\n}\n\n/**\n * Get formatted answer for a field (Markdown only)\n */\nexport function getAnswerMarkdown(\n field: Field,\n state: FormState,\n options: {\n format:\n | 'email' // GOV.UK Notify emails\n | 'summary' // Check answers summary\n } = { format: 'summary' }\n) {\n const answer = field.getDisplayStringFromState(state)\n\n // Use escaped display text\n let answerEscaped = `${escapeMarkdown(answer)}\\n`\n\n if (field instanceof Components.FileUploadField) {\n const files = field.getFormValueFromState(state)\n\n // Skip empty files\n if (!files?.length) {\n return answerEscaped\n }\n\n answerEscaped = `${escapeMarkdown(answer)}:\\n\\n`\n\n // Append bullet points\n answerEscaped += files\n .map(({ status }) => {\n const { file } = status.form\n const filename = escapeMarkdown(file.filename)\n return `* [${filename}](${designerUrl}/file-download/${file.fileId})\\n`\n })\n .join('')\n } else if (field instanceof ListFormComponent) {\n const values = [field.getContextValueFromState(state)].flat()\n const items = field.items.filter(({ value }) => values.includes(value))\n\n // Skip empty values\n if (!items.length) {\n return answerEscaped\n }\n\n answerEscaped = ''\n\n // Append bullet points\n answerEscaped += items\n .map((item) => {\n const label = escapeMarkdown(item.text)\n const value = escapeMarkdown(`(${item.value})`)\n\n let line = label\n\n // Prepend bullet points for checkboxes only\n if (field instanceof Components.CheckboxesField) {\n line = `* ${line}`\n }\n\n // Append raw values in parentheses\n // e.g. `* None of the above (false)`\n return options.format === 'email' &&\n `${item.value}`.toLowerCase() !== item.text.toLowerCase()\n ? `${line} ${value}\\n`\n : `${line}\\n`\n })\n .join('')\n } else if (field instanceof Components.MultilineTextField) {\n // Preserve Multiline text new lines\n answerEscaped = answer\n .split(/\\r?\\n/)\n .map(escapeMarkdown)\n .join('\\n')\n .concat('\\n')\n } else if (field instanceof Components.UkAddressField) {\n // Format UK addresses into new lines\n answerEscaped = (field.getContextValueFromState(state) ?? [])\n .map(escapeMarkdown)\n .join('\\n')\n .concat('\\n')\n }\n\n return answerEscaped\n}\n"],"mappings":"AAAA,SAASA,aAAa,QAA2B,oBAAoB;AACrE,SAASC,MAAM,QAAoB,QAAQ;AAE3C,SAASC,MAAM;AAEf,SAASC,iBAAiB;AAC1B,SAASC,cAAc;AACvB,OAAO,KAAKC,UAAU;;AAGtB;;AAKA;;AAkBA;;AASA;;AASA,OAAO,MAAMC,WAAW,GAAGJ,MAAM,CAACK,GAAG,CAAC,aAAa,CAAC;AAEpD,OAAO,MAAMC,QAAQ,GAAG,IAAIP,MAAM,CAAC;EACjCQ,MAAM,EAAE,IAAI;EACZC,GAAG,EAAE,IAAI;EAET;AACF;AACA;AACA;EACEC,UAAU,EAAE,CACV;IACEC,IAAI,EAAE,WAAW;IACjBC,QAAQA,CAAC;MAAEC,MAAM,GAAG;IAAG,CAAC,EAAE;MACxB,MAAMC,IAAI,GAAG,IAAI,CAACC,MAAM,CAACC,WAAW,CAACH,MAAM,CAAC;MAC5C,OAAOA,MAAM,CAACI,MAAM,GAAG,CAAC,GAAG,GAAGH,IAAI,MAAM,GAAGA,IAAI;IACjD;EACF,CAAC,CACF;EAED;AACF;AACA;EACEI,UAAUA,CAACC,KAAK,EAAE;IAChB,MAAMN,MAAuB,GAAG,CAC9B,IAAI,EACJ,QAAQ,EACR,MAAM,EACN,WAAW,EACX,WAAW,EACX,OAAO,EACP,MAAM,CACP;IAED,IAAI,CAACA,MAAM,CAACO,QAAQ,CAACD,KAAK,CAACE,IAAI,CAAC,EAAE;MAChCF,KAAK,CAACE,IAAI,GAAG,MAAM;IACrB;EACF;AACF,CAAC,CAAC;;AAEF;AACA;AACA;AACA,OAAO,SAASC,gBAAgBA,CAC9BC,KAA0B,EACE;EAC5B,OAAO,CAAC,CAACA,KAAK,IAAIC,eAAe,CAACD,KAAK,CAACF,IAAI,CAAC;AAC/C;AAEA,OAAO,SAASG,eAAeA,CAC7BH,IAAoB,EACO;EAC3B,MAAMI,YAAY,GAAG,CACnB1B,aAAa,CAAC2B,iBAAiB,EAC/B3B,aAAa,CAAC4B,eAAe,EAC7B5B,aAAa,CAAC6B,WAAW,EACzB7B,aAAa,CAAC8B,WAAW,EACzB9B,aAAa,CAAC+B,UAAU,CACzB;EAED,OAAO,CAAC,CAACT,IAAI,IAAII,YAAY,CAACL,QAAQ,CAACC,IAAI,CAAC;AAC9C;;AAEA;AACA;AACA;AACA,OAAO,SAASU,eAAeA,CAC7BC,GAAiB,EACjBC,OAAuD,EAC5C;EACX,IAAIC,SAAgC;EAEpC,QAAQF,GAAG,CAACX,IAAI;IACd,KAAKtB,aAAa,CAAC2B,iBAAiB;MAClCQ,SAAS,GAAG,IAAI9B,UAAU,CAACsB,iBAAiB,CAACM,GAAG,EAAEC,OAAO,CAAC;MAC1D;IAEF,KAAKlC,aAAa,CAAC4B,eAAe;MAChCO,SAAS,GAAG,IAAI9B,UAAU,CAACuB,eAAe,CAACK,GAAG,EAAEC,OAAO,CAAC;MACxD;IAEF,KAAKlC,aAAa,CAACoC,cAAc;MAC/BD,SAAS,GAAG,IAAI9B,UAAU,CAAC+B,cAAc,CAACH,GAAG,EAAEC,OAAO,CAAC;MACvD;IAEF,KAAKlC,aAAa,CAACqC,OAAO;MACxBF,SAAS,GAAG,IAAI9B,UAAU,CAACgC,OAAO,CAACJ,GAAG,EAAEC,OAAO,CAAC;MAChD;IAEF,KAAKlC,aAAa,CAACsC,iBAAiB;MAClCH,SAAS,GAAG,IAAI9B,UAAU,CAACiC,iBAAiB,CAACL,GAAG,EAAEC,OAAO,CAAC;MAC1D;IAEF,KAAKlC,aAAa,CAACuC,IAAI;MACrBJ,SAAS,GAAG,IAAI9B,UAAU,CAACkC,IAAI,CAACN,GAAG,EAAEC,OAAO,CAAC;MAC7C;IAEF,KAAKlC,aAAa,CAACwC,SAAS;MAC1BL,SAAS,GAAG,IAAI9B,UAAU,CAACmC,SAAS,CAACP,GAAG,EAAEC,OAAO,CAAC;MAClD;IAEF,KAAKlC,aAAa,CAACyC,IAAI;MACrBN,SAAS,GAAG,IAAI9B,UAAU,CAACoC,IAAI,CAACR,GAAG,EAAEC,OAAO,CAAC;MAC7C;IAEF,KAAKlC,aAAa,CAAC0C,QAAQ;MACzBP,SAAS,GAAG,IAAI9B,UAAU,CAACqC,QAAQ,CAACT,GAAG,EAAEC,OAAO,CAAC;MACjD;IAEF,KAAKlC,aAAa,CAAC2C,kBAAkB;MACnCR,SAAS,GAAG,IAAI9B,UAAU,CAACsC,kBAAkB,CAACV,GAAG,EAAEC,OAAO,CAAC;MAC3D;IAEF,KAAKlC,aAAa,CAAC4C,WAAW;MAC5BT,SAAS,GAAG,IAAI9B,UAAU,CAACuC,WAAW,CAACX,GAAG,EAAEC,OAAO,CAAC;MACpD;IAEF,KAAKlC,aAAa,CAAC6B,WAAW;MAC5BM,SAAS,GAAG,IAAI9B,UAAU,CAACwB,WAAW,CAACI,GAAG,EAAEC,OAAO,CAAC;MACpD;IAEF,KAAKlC,aAAa,CAAC8B,WAAW;MAC5BK,SAAS,GAAG,IAAI9B,UAAU,CAACyB,WAAW,CAACG,GAAG,EAAEC,OAAO,CAAC;MACpD;IAEF,KAAKlC,aAAa,CAAC6C,oBAAoB;MACrCV,SAAS,GAAG,IAAI9B,UAAU,CAACwC,oBAAoB,CAACZ,GAAG,EAAEC,OAAO,CAAC;MAC7D;IAEF,KAAKlC,aAAa,CAAC8C,SAAS;MAC1BX,SAAS,GAAG,IAAI9B,UAAU,CAACyC,SAAS,CAACb,GAAG,EAAEC,OAAO,CAAC;MAClD;IAEF,KAAKlC,aAAa,CAAC+C,cAAc;MAC/BZ,SAAS,GAAG,IAAI9B,UAAU,CAAC0C,cAAc,CAACd,GAAG,EAAEC,OAAO,CAAC;MACvD;IAEF,KAAKlC,aAAa,CAAC+B,UAAU;MAC3BI,SAAS,GAAG,IAAI9B,UAAU,CAAC0B,UAAU,CAACE,GAAG,EAAEC,OAAO,CAAC;MACnD;IAEF,KAAKlC,aAAa,CAACgD,cAAc;MAC/Bb,SAAS,GAAG,IAAI9B,UAAU,CAAC2C,cAAc,CAACf,GAAG,EAAEC,OAAO,CAAC;MACvD;IAEF,KAAKlC,aAAa,CAACiD,eAAe;MAChCd,SAAS,GAAG,IAAI9B,UAAU,CAAC4C,eAAe,CAAChB,GAAG,EAAEC,OAAO,CAAC;MACxD;EACJ;EAEA,IAAI,OAAOC,SAAS,KAAK,WAAW,EAAE;IACpC,MAAM,IAAIe,KAAK,CAAC,kBAAkBjB,GAAG,CAACX,IAAI,iBAAiB,CAAC;EAC9D;EAEA,OAAOa,SAAS;AAClB;;AAEA;AACA;AACA;AACA,OAAO,SAASgB,SAASA,CACvB3B,KAAY,EACZ4B,KAAgB,EAChBlB,OAKC,GAAG;EAAEmB,MAAM,EAAE;AAAU,CAAC,EACzB;EACA;EACA,IAAInB,OAAO,CAACmB,MAAM,KAAK,OAAO,EAAE;IAC9B,OAAOC,iBAAiB,CAAC9B,KAAK,EAAE4B,KAAK,EAAE;MAAEC,MAAM,EAAE;IAAQ,CAAC,CAAC;EAC7D;;EAEA;EACA,IAAInB,OAAO,CAACmB,MAAM,KAAK,MAAM,EAAE;IAC7B,MAAME,OAAO,GAAG/B,KAAK,CAACgC,wBAAwB,CAACJ,KAAK,CAAC;IACrD,OAAOG,OAAO,EAAEE,QAAQ,CAAC,CAAC,IAAI,EAAE;EAClC;;EAEA;EACA,IACEjC,KAAK,YAAYrB,iBAAiB,IAClCqB,KAAK,YAAYnB,UAAU,CAACsC,kBAAkB,IAC9CnB,KAAK,YAAYnB,UAAU,CAAC0C,cAAc,EAC1C;IACA,OAAOvC,QAAQ,CACZkD,KAAK,CAACJ,iBAAiB,CAAC9B,KAAK,EAAE4B,KAAK,CAAC,EAAE;MAAEO,KAAK,EAAE;IAAM,CAAC,CAAC,CACxDC,IAAI,CAAC,CAAC;EACX;;EAEA;EACA,OAAOpC,KAAK,CAACqC,yBAAyB,CAACT,KAAK,CAAC;AAC/C;;AAEA;AACA;AACA;AACA,OAAO,SAASE,iBAAiBA,CAC/B9B,KAAY,EACZ4B,KAAgB,EAChBlB,OAIC,GAAG;EAAEmB,MAAM,EAAE;AAAU,CAAC,EACzB;EACA,MAAMS,MAAM,GAAGtC,KAAK,CAACqC,yBAAyB,CAACT,KAAK,CAAC;;EAErD;EACA,IAAIW,aAAa,GAAG,GAAG3D,cAAc,CAAC0D,MAAM,CAAC,IAAI;EAEjD,IAAItC,KAAK,YAAYnB,UAAU,CAAC4C,eAAe,EAAE;IAC/C,MAAMe,KAAK,GAAGxC,KAAK,CAACyC,qBAAqB,CAACb,KAAK,CAAC;;IAEhD;IACA,IAAI,CAACY,KAAK,EAAE9C,MAAM,EAAE;MAClB,OAAO6C,aAAa;IACtB;IAEAA,aAAa,GAAG,GAAG3D,cAAc,CAAC0D,MAAM,CAAC,OAAO;;IAEhD;IACAC,aAAa,IAAIC,KAAK,CACnBE,GAAG,CAAC,CAAC;MAAEC;IAAO,CAAC,KAAK;MACnB,MAAM;QAAEC;MAAK,CAAC,GAAGD,MAAM,CAACE,IAAI;MAC5B,MAAMC,QAAQ,GAAGlE,cAAc,CAACgE,IAAI,CAACE,QAAQ,CAAC;MAC9C,OAAO,MAAMA,QAAQ,KAAKhE,WAAW,kBAAkB8D,IAAI,CAACG,MAAM,KAAK;IACzE,CAAC,CAAC,CACDC,IAAI,CAAC,EAAE,CAAC;EACb,CAAC,MAAM,IAAIhD,KAAK,YAAYrB,iBAAiB,EAAE;IAC7C,MAAMsE,MAAM,GAAG,CAACjD,KAAK,CAACgC,wBAAwB,CAACJ,KAAK,CAAC,CAAC,CAACsB,IAAI,CAAC,CAAC;IAC7D,MAAMC,KAAK,GAAGnD,KAAK,CAACmD,KAAK,CAACC,MAAM,CAAC,CAAC;MAAEC;IAAM,CAAC,KAAKJ,MAAM,CAACpD,QAAQ,CAACwD,KAAK,CAAC,CAAC;;IAEvE;IACA,IAAI,CAACF,KAAK,CAACzD,MAAM,EAAE;MACjB,OAAO6C,aAAa;IACtB;IAEAA,aAAa,GAAG,EAAE;;IAElB;IACAA,aAAa,IAAIY,KAAK,CACnBT,GAAG,CAAEY,IAAI,IAAK;MACb,MAAMC,KAAK,GAAG3E,cAAc,CAAC0E,IAAI,CAAC/D,IAAI,CAAC;MACvC,MAAM8D,KAAK,GAAGzE,cAAc,CAAC,IAAI0E,IAAI,CAACD,KAAK,GAAG,CAAC;MAE/C,IAAIG,IAAI,GAAGD,KAAK;;MAEhB;MACA,IAAIvD,KAAK,YAAYnB,UAAU,CAACuB,eAAe,EAAE;QAC/CoD,IAAI,GAAG,KAAKA,IAAI,EAAE;MACpB;;MAEA;MACA;MACA,OAAO9C,OAAO,CAACmB,MAAM,KAAK,OAAO,IAC/B,GAAGyB,IAAI,CAACD,KAAK,EAAE,CAACI,WAAW,CAAC,CAAC,KAAKH,IAAI,CAAC/D,IAAI,CAACkE,WAAW,CAAC,CAAC,GACvD,GAAGD,IAAI,IAAIH,KAAK,IAAI,GACpB,GAAGG,IAAI,IAAI;IACjB,CAAC,CAAC,CACDR,IAAI,CAAC,EAAE,CAAC;EACb,CAAC,MAAM,IAAIhD,KAAK,YAAYnB,UAAU,CAACsC,kBAAkB,EAAE;IACzD;IACAoB,aAAa,GAAGD,MAAM,CACnBoB,KAAK,CAAC,OAAO,CAAC,CACdhB,GAAG,CAAC9D,cAAc,CAAC,CACnBoE,IAAI,CAAC,IAAI,CAAC,CACVW,MAAM,CAAC,IAAI,CAAC;EACjB,CAAC,MAAM,IAAI3D,KAAK,YAAYnB,UAAU,CAAC0C,cAAc,EAAE;IACrD;IACAgB,aAAa,GAAG,CAACvC,KAAK,CAACgC,wBAAwB,CAACJ,KAAK,CAAC,IAAI,EAAE,EACzDc,GAAG,CAAC9D,cAAc,CAAC,CACnBoE,IAAI,CAAC,IAAI,CAAC,CACVW,MAAM,CAAC,IAAI,CAAC;EACjB;EAEA,OAAOpB,aAAa;AACtB","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"components.js","names":["ComponentType","Marked","config","ListFormComponent","escapeMarkdown","Components","designerUrl","get","markdown","breaks","gfm","extensions","name","renderer","tokens","text","parser","parseInline","length","walkTokens","token","includes","type","hasListFormField","field","isListFieldType","allowedTypes","AutocompleteField","CheckboxesField","RadiosField","SelectField","YesNoField","createComponent","def","options","component","DatePartsField","Details","EmailAddressField","Html","InsetText","List","Markdown","MultilineTextField","NumberField","TelephoneNumberField","TextField","UkAddressField","MonthYearField","FileUploadField","Error","getAnswer","state","format","getAnswerMarkdown","context","getContextValueFromState","toString","parse","async","trim","getDisplayStringFromState","answer","answerEscaped","files","getFormValueFromState","map","status","file","form","filename","fileId","join","values","flat","items","filter","value","item","label","line","toLowerCase","split","concat"],"sources":["../../../../../../src/server/plugins/engine/components/helpers/components.ts"],"sourcesContent":["import { ComponentType, type ComponentDef } from '@defra/forms-model'\nimport { Marked, type Token } from 'marked'\n\nimport { config } from '~/src/config/index.js'\nimport { type ComponentBase } from '~/src/server/plugins/engine/components/ComponentBase.js'\nimport { ListFormComponent } from '~/src/server/plugins/engine/components/ListFormComponent.js'\nimport { escapeMarkdown } from '~/src/server/plugins/engine/components/helpers/index.js'\nimport * as Components from '~/src/server/plugins/engine/components/index.js'\nimport { type FormState } from '~/src/server/plugins/engine/types.js'\n\n// All component instances\nexport type Component = InstanceType<\n (typeof Components)[keyof typeof Components]\n>\n\n// Field component instances only\nexport type Field = InstanceType<\n | typeof Components.AutocompleteField\n | typeof Components.RadiosField\n | typeof Components.YesNoField\n | typeof Components.CheckboxesField\n | typeof Components.DatePartsField\n | typeof Components.EmailAddressField\n | typeof Components.MonthYearField\n | typeof Components.MultilineTextField\n | typeof Components.NumberField\n | typeof Components.SelectField\n | typeof Components.TelephoneNumberField\n | typeof Components.TextField\n | typeof Components.UkAddressField\n | typeof Components.FileUploadField\n>\n\n// Guidance component instances only\nexport type Guidance = InstanceType<\n | typeof Components.Details\n | typeof Components.Html\n | typeof Components.Markdown\n | typeof Components.InsetText\n | typeof Components.List\n>\n\n// List component instances only\nexport type ListField = InstanceType<\n | typeof Components.AutocompleteField\n | typeof Components.CheckboxesField\n | typeof Components.RadiosField\n | typeof Components.SelectField\n | typeof Components.YesNoField\n>\n\nexport const designerUrl = config.get('designerUrl')\n\nexport const markdown = new Marked({\n breaks: true,\n gfm: true,\n\n /**\n * Render paragraphs without `<p>` wrappers\n * for check answers summary list `<dd>`\n */\n extensions: [\n {\n name: 'paragraph',\n renderer({ tokens = [] }) {\n const text = this.parser.parseInline(tokens)\n return tokens.length > 1 ? `${text}<br>` : text\n }\n }\n ],\n\n /**\n * Restrict allowed Markdown tokens\n */\n walkTokens(token) {\n const tokens: Token['type'][] = [\n 'br',\n 'escape',\n 'list',\n 'list_item',\n 'paragraph',\n 'space',\n 'text'\n ]\n\n if (!tokens.includes(token.type)) {\n token.type = 'text'\n }\n }\n})\n\n/**\n * Filter known components with lists\n */\nexport function hasListFormField(\n field?: Partial<Component>\n): field is ListFormComponent {\n return !!field && isListFieldType(field.type)\n}\n\nexport function isListFieldType(\n type?: ComponentType\n): type is ListField['type'] {\n const allowedTypes = [\n ComponentType.AutocompleteField,\n ComponentType.CheckboxesField,\n ComponentType.RadiosField,\n ComponentType.SelectField,\n ComponentType.YesNoField\n ]\n\n return !!type && allowedTypes.includes(type)\n}\n\n/**\n * Create field instance for each {@link ComponentDef} type\n */\nexport function createComponent(\n def: ComponentDef,\n options: ConstructorParameters<typeof ComponentBase>[1]\n): Component {\n let component: Component | undefined\n\n switch (def.type) {\n case ComponentType.AutocompleteField:\n component = new Components.AutocompleteField(def, options)\n break\n\n case ComponentType.CheckboxesField:\n component = new Components.CheckboxesField(def, options)\n break\n\n case ComponentType.DatePartsField:\n component = new Components.DatePartsField(def, options)\n break\n\n case ComponentType.Details:\n component = new Components.Details(def, options)\n break\n\n case ComponentType.EmailAddressField:\n component = new Components.EmailAddressField(def, options)\n break\n\n case ComponentType.Html:\n component = new Components.Html(def, options)\n break\n\n case ComponentType.InsetText:\n component = new Components.InsetText(def, options)\n break\n\n case ComponentType.List:\n component = new Components.List(def, options)\n break\n\n case ComponentType.Markdown:\n component = new Components.Markdown(def, options)\n break\n\n case ComponentType.MultilineTextField:\n component = new Components.MultilineTextField(def, options)\n break\n\n case ComponentType.NumberField:\n component = new Components.NumberField(def, options)\n break\n\n case ComponentType.RadiosField:\n component = new Components.RadiosField(def, options)\n break\n\n case ComponentType.SelectField:\n component = new Components.SelectField(def, options)\n break\n\n case ComponentType.TelephoneNumberField:\n component = new Components.TelephoneNumberField(def, options)\n break\n\n case ComponentType.TextField:\n component = new Components.TextField(def, options)\n break\n\n case ComponentType.UkAddressField:\n component = new Components.UkAddressField(def, options)\n break\n\n case ComponentType.YesNoField:\n component = new Components.YesNoField(def, options)\n break\n\n case ComponentType.MonthYearField:\n component = new Components.MonthYearField(def, options)\n break\n\n case ComponentType.FileUploadField:\n component = new Components.FileUploadField(def, options)\n break\n }\n\n if (typeof component === 'undefined') {\n throw new Error(`Component type ${def.type} does not exist`)\n }\n\n return component\n}\n\n/**\n * Get formatted answer for a field\n */\nexport function getAnswer(\n field: Field,\n state: FormState,\n options: {\n format:\n | 'data' // Submission data\n | 'email' // GOV.UK Notify emails\n | 'summary' // Check answers summary\n } = { format: 'summary' }\n) {\n // Use escaped display text for GOV.UK Notify emails\n if (options.format === 'email') {\n return getAnswerMarkdown(field, state, { format: 'email' })\n }\n\n // Use context value for submission data\n if (options.format === 'data') {\n const context = field.getContextValueFromState(state)\n return context?.toString() ?? ''\n }\n\n // Use display HTML for check answers summary (multi line)\n if (\n field instanceof ListFormComponent ||\n field instanceof Components.MultilineTextField ||\n field instanceof Components.UkAddressField\n ) {\n return markdown\n .parse(getAnswerMarkdown(field, state), { async: false })\n .trim()\n }\n\n // Use display text for check answers summary (single line)\n return field.getDisplayStringFromState(state)\n}\n\n/**\n * Get formatted answer for a field (Markdown only)\n */\nexport function getAnswerMarkdown(\n field: Field,\n state: FormState,\n options: {\n format:\n | 'email' // GOV.UK Notify emails\n | 'summary' // Check answers summary\n } = { format: 'summary' }\n) {\n const answer = field.getDisplayStringFromState(state)\n\n // Use escaped display text\n let answerEscaped = `${escapeMarkdown(answer)}\\n`\n\n if (field instanceof Components.FileUploadField) {\n const files = field.getFormValueFromState(state)\n\n // Skip empty files\n if (!files?.length) {\n return answerEscaped\n }\n\n answerEscaped = `${escapeMarkdown(answer)}:\\n\\n`\n\n // Append bullet points\n answerEscaped += files\n .map(({ status }) => {\n const { file } = status.form\n const filename = escapeMarkdown(file.filename)\n return `* [${filename}](${designerUrl}/file-download/${file.fileId})\\n`\n })\n .join('')\n } else if (field instanceof ListFormComponent) {\n const values = [field.getContextValueFromState(state)].flat()\n const items = field.items.filter(({ value }) => values.includes(value))\n\n // Skip empty values\n if (!items.length) {\n return answerEscaped\n }\n\n answerEscaped = ''\n\n // Append bullet points\n answerEscaped += items\n .map((item) => {\n const label = escapeMarkdown(item.text)\n const value = escapeMarkdown(`(${item.value})`)\n\n let line = label\n\n // Prepend bullet points for checkboxes only\n if (field instanceof Components.CheckboxesField) {\n line = `* ${line}`\n }\n\n // Append raw values in parentheses\n // e.g. `* None of the above (false)`\n return options.format === 'email' &&\n `${item.value}`.toLowerCase() !== item.text.toLowerCase()\n ? `${line} ${value}\\n`\n : `${line}\\n`\n })\n .join('')\n } else if (field instanceof Components.MultilineTextField) {\n // Preserve Multiline text new lines\n answerEscaped = answer\n .split(/(?:\\r?\\n)+/)\n .map(escapeMarkdown)\n .join('\\n')\n .concat('\\n')\n } else if (field instanceof Components.UkAddressField) {\n // Format UK addresses into new lines\n answerEscaped = (field.getContextValueFromState(state) ?? [])\n .map(escapeMarkdown)\n .join('\\n')\n .concat('\\n')\n }\n\n return answerEscaped\n}\n"],"mappings":"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","ignoreList":[]}
|
package/package.json
CHANGED
|
@@ -161,6 +161,17 @@ describe('MultilineTextField', () => {
|
|
|
161
161
|
expect(answer2).toBe('')
|
|
162
162
|
})
|
|
163
163
|
|
|
164
|
+
it('returns multiline text from state, collapsing multiple newlines into one', () => {
|
|
165
|
+
const state1 = getFormState('Line 1\r\nLine 2\r\nLine 3')
|
|
166
|
+
const state2 = getFormState('Line 1\r\n\r\nLine 2\r\n\r\n\r\nLine 3')
|
|
167
|
+
|
|
168
|
+
const answer1 = getAnswer(field, state1)
|
|
169
|
+
const answer2 = getAnswer(field, state2)
|
|
170
|
+
|
|
171
|
+
expect(answer1).toBe('Line 1<br>Line 2<br>Line 3<br>')
|
|
172
|
+
expect(answer2).toBe('Line 1<br>Line 2<br>Line 3<br>')
|
|
173
|
+
})
|
|
174
|
+
|
|
164
175
|
it('returns payload from state', () => {
|
|
165
176
|
const state1 = getFormState('Textarea')
|
|
166
177
|
const state2 = getFormState(null)
|
|
@@ -315,7 +315,7 @@ export function getAnswerMarkdown(
|
|
|
315
315
|
} else if (field instanceof Components.MultilineTextField) {
|
|
316
316
|
// Preserve Multiline text new lines
|
|
317
317
|
answerEscaped = answer
|
|
318
|
-
.split(
|
|
318
|
+
.split(/(?:\r?\n)+/)
|
|
319
319
|
.map(escapeMarkdown)
|
|
320
320
|
.join('\n')
|
|
321
321
|
.concat('\n')
|