@defra/forms-engine-plugin 4.0.32 → 4.0.33

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.
@@ -9,7 +9,7 @@ export declare class CheckboxesField extends SelectionControlField {
9
9
  constructor(def: CheckboxesFieldComponent, props: ConstructorParameters<typeof SelectionControlField>[1]);
10
10
  getFormValueFromState(state: FormSubmissionState): (string | number | boolean)[] | undefined;
11
11
  getFormValue(value?: FormStateValue | FormState): (string | number | boolean)[] | undefined;
12
- getDisplayStringFromFormValue(selected: (string | number | boolean)[]): string;
12
+ getDisplayStringFromFormValue(selected: (string | number | boolean)[] | undefined): string;
13
13
  getContextValueFromFormValue(values: (string | number | boolean)[] | undefined): (string | number | boolean)[];
14
14
  getDisplayStringFromState(state: FormSubmissionState): string;
15
15
  getContextValueFromState(state: FormSubmissionState): (string | number | boolean)[];
@@ -40,6 +40,9 @@ export class CheckboxesField extends SelectionControlField {
40
40
  const {
41
41
  items
42
42
  } = this;
43
+ if (!selected) {
44
+ return '';
45
+ }
43
46
 
44
47
  // Map selected values to text
45
48
  return items.filter(item => selected.includes(item.value)).map(item => item.text).join(', ');
@@ -1 +1 @@
1
- {"version":3,"file":"CheckboxesField.js","names":["joi","isFormValue","SelectionControlField","CheckboxesField","constructor","def","props","listType","type","options","formSchema","array","itemsSchema","valid","values","label","items","single","required","optional","default","stateSchema","allow","getFormValueFromState","state","name","getFormValue","selected","filter","item","includes","value","map","length","undefined","isValue","getDisplayStringFromFormValue","text","join","getContextValueFromFormValue","getDisplayStringFromState","getContextValueFromState","Array","isArray","every"],"sources":["../../../../../src/server/plugins/engine/components/CheckboxesField.ts"],"sourcesContent":["import { type CheckboxesFieldComponent, type Item } from '@defra/forms-model'\nimport joi, { type ArraySchema } from 'joi'\n\nimport { isFormValue } from '~/src/server/plugins/engine/components/FormComponent.js'\nimport { SelectionControlField } from '~/src/server/plugins/engine/components/SelectionControlField.js'\nimport { type FormModel } from '~/src/server/plugins/engine/models/FormModel.js'\nimport { type QuestionPageController } from '~/src/server/plugins/engine/pageControllers/QuestionPageController.js'\nimport {\n type FormState,\n type FormStateValue,\n type FormSubmissionState\n} from '~/src/server/plugins/engine/types.js'\n\nexport class CheckboxesField extends SelectionControlField {\n declare options: CheckboxesFieldComponent['options']\n declare formSchema: ArraySchema<string> | ArraySchema<number>\n declare stateSchema: ArraySchema<string> | ArraySchema<number>\n\n constructor(\n def: CheckboxesFieldComponent,\n props: ConstructorParameters<typeof SelectionControlField>[1]\n ) {\n super(def, props)\n\n const { listType: type } = this\n const { options } = def\n\n let formSchema =\n type === 'string' ? joi.array<string>() : joi.array<number>()\n\n const itemsSchema = joi[type]()\n .valid(...this.values)\n .label(this.label)\n\n formSchema = formSchema\n .items(itemsSchema)\n .single()\n .label(this.label)\n .required()\n\n if (options.required === false) {\n formSchema = formSchema.optional()\n }\n\n this.formSchema = formSchema.default([])\n this.stateSchema = formSchema.default(null).allow(null)\n this.options = options\n }\n\n getFormValueFromState(state: FormSubmissionState) {\n const { items, name } = this\n\n // State checkbox values\n const values = this.getFormValue(state[name]) ?? []\n\n // Map (or discard) state values to item values\n const selected = items\n .filter((item) => values.includes(item.value))\n .map((item) => item.value)\n\n return selected.length ? selected : undefined\n }\n\n getFormValue(value?: FormStateValue | FormState) {\n return this.isValue(value) ? value : undefined\n }\n\n getDisplayStringFromFormValue(selected: (string | number | boolean)[]) {\n const { items } = this\n\n // Map selected values to text\n return items\n .filter((item) => selected.includes(item.value))\n .map((item) => item.text)\n .join(', ')\n }\n\n getContextValueFromFormValue(\n values: (string | number | boolean)[] | undefined\n ): (string | number | boolean)[] {\n /**\n * For evaluation context purposes, optional {@link CheckboxesField}\n * with an undefined value (i.e. nothing selected) should default to [].\n * This way conditions are not evaluated against `undefined` which throws errors.\n * Currently these errors are caught and the evaluation returns default `false`.\n * @see {@link QuestionPageController.getNextPath} for `undefined` return value\n * @see {@link FormModel.makeCondition} for try/catch block with default `false`\n * For negative conditions this is a problem because E.g.\n * The condition: 'selectedchecks' does not contain 'someval'\n * should return true IF 'selectedchecks' is undefined, not throw and return false.\n */\n return values ?? []\n }\n\n getDisplayStringFromState(state: FormSubmissionState) {\n // Selected checkbox values\n const selected = this.getFormValueFromState(state) ?? []\n\n // Map selected values to text\n return this.getDisplayStringFromFormValue(selected)\n }\n\n getContextValueFromState(state: FormSubmissionState) {\n const values = this.getFormValueFromState(state)\n\n return this.getContextValueFromFormValue(values)\n }\n\n isValue(value?: FormStateValue | FormState): value is Item['value'][] {\n if (!Array.isArray(value)) {\n return false\n }\n\n // Skip checks when empty\n if (!value.length) {\n return true\n }\n\n return value.every(isFormValue)\n }\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"CheckboxesField.js","names":["joi","isFormValue","SelectionControlField","CheckboxesField","constructor","def","props","listType","type","options","formSchema","array","itemsSchema","valid","values","label","items","single","required","optional","default","stateSchema","allow","getFormValueFromState","state","name","getFormValue","selected","filter","item","includes","value","map","length","undefined","isValue","getDisplayStringFromFormValue","text","join","getContextValueFromFormValue","getDisplayStringFromState","getContextValueFromState","Array","isArray","every"],"sources":["../../../../../src/server/plugins/engine/components/CheckboxesField.ts"],"sourcesContent":["import { type CheckboxesFieldComponent, type Item } from '@defra/forms-model'\nimport joi, { type ArraySchema } from 'joi'\n\nimport { isFormValue } from '~/src/server/plugins/engine/components/FormComponent.js'\nimport { SelectionControlField } from '~/src/server/plugins/engine/components/SelectionControlField.js'\nimport { type FormModel } from '~/src/server/plugins/engine/models/FormModel.js'\nimport { type QuestionPageController } from '~/src/server/plugins/engine/pageControllers/QuestionPageController.js'\nimport {\n type FormState,\n type FormStateValue,\n type FormSubmissionState\n} from '~/src/server/plugins/engine/types.js'\n\nexport class CheckboxesField extends SelectionControlField {\n declare options: CheckboxesFieldComponent['options']\n declare formSchema: ArraySchema<string> | ArraySchema<number>\n declare stateSchema: ArraySchema<string> | ArraySchema<number>\n\n constructor(\n def: CheckboxesFieldComponent,\n props: ConstructorParameters<typeof SelectionControlField>[1]\n ) {\n super(def, props)\n\n const { listType: type } = this\n const { options } = def\n\n let formSchema =\n type === 'string' ? joi.array<string>() : joi.array<number>()\n\n const itemsSchema = joi[type]()\n .valid(...this.values)\n .label(this.label)\n\n formSchema = formSchema\n .items(itemsSchema)\n .single()\n .label(this.label)\n .required()\n\n if (options.required === false) {\n formSchema = formSchema.optional()\n }\n\n this.formSchema = formSchema.default([])\n this.stateSchema = formSchema.default(null).allow(null)\n this.options = options\n }\n\n getFormValueFromState(state: FormSubmissionState) {\n const { items, name } = this\n\n // State checkbox values\n const values = this.getFormValue(state[name]) ?? []\n\n // Map (or discard) state values to item values\n const selected = items\n .filter((item) => values.includes(item.value))\n .map((item) => item.value)\n\n return selected.length ? selected : undefined\n }\n\n getFormValue(value?: FormStateValue | FormState) {\n return this.isValue(value) ? value : undefined\n }\n\n getDisplayStringFromFormValue(\n selected: (string | number | boolean)[] | undefined\n ) {\n const { items } = this\n\n if (!selected) {\n return ''\n }\n\n // Map selected values to text\n return items\n .filter((item) => selected.includes(item.value))\n .map((item) => item.text)\n .join(', ')\n }\n\n getContextValueFromFormValue(\n values: (string | number | boolean)[] | undefined\n ): (string | number | boolean)[] {\n /**\n * For evaluation context purposes, optional {@link CheckboxesField}\n * with an undefined value (i.e. nothing selected) should default to [].\n * This way conditions are not evaluated against `undefined` which throws errors.\n * Currently these errors are caught and the evaluation returns default `false`.\n * @see {@link QuestionPageController.getNextPath} for `undefined` return value\n * @see {@link FormModel.makeCondition} for try/catch block with default `false`\n * For negative conditions this is a problem because E.g.\n * The condition: 'selectedchecks' does not contain 'someval'\n * should return true IF 'selectedchecks' is undefined, not throw and return false.\n */\n return values ?? []\n }\n\n getDisplayStringFromState(state: FormSubmissionState) {\n // Selected checkbox values\n const selected = this.getFormValueFromState(state) ?? []\n\n // Map selected values to text\n return this.getDisplayStringFromFormValue(selected)\n }\n\n getContextValueFromState(state: FormSubmissionState) {\n const values = this.getFormValueFromState(state)\n\n return this.getContextValueFromFormValue(values)\n }\n\n isValue(value?: FormStateValue | FormState): value is Item['value'][] {\n if (!Array.isArray(value)) {\n return false\n }\n\n // Skip checks when empty\n if (!value.length) {\n return true\n }\n\n return value.every(isFormValue)\n }\n}\n"],"mappings":"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","ignoreList":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@defra/forms-engine-plugin",
3
- "version": "4.0.32",
3
+ "version": "4.0.33",
4
4
  "description": "Defra forms engine",
5
5
  "type": "module",
6
6
  "files": [
@@ -41,24 +41,6 @@ describe.each([
41
41
  deny: ['5', '6', '7', '8']
42
42
  }
43
43
  },
44
- {
45
- component: {
46
- title: 'String list title',
47
- shortDescription: 'String list',
48
- name: 'myComponent',
49
- type: ComponentType.CheckboxesField,
50
- list: 'listString',
51
- options: {}
52
- } satisfies CheckboxesFieldComponent,
53
-
54
- options: {
55
- label: 'string list',
56
- list: listString,
57
- examples: listStringExamples,
58
- allow: ['1', '2', '3', '4'],
59
- deny: ['5', '6', '7', '8']
60
- }
61
- },
62
44
  {
63
45
  component: {
64
46
  title: 'Number list title',
@@ -407,5 +389,43 @@ describe.each([
407
389
  expect(errors.advancedSettingsErrors).toBeEmpty()
408
390
  })
409
391
  })
392
+
393
+ describe('getDisplayStringFromFormValue', () => {
394
+ it('returns empty string when value is undefined', () => {
395
+ const checkboxField = field as CheckboxesField
396
+ const result = checkboxField.getDisplayStringFromFormValue(undefined)
397
+ expect(result).toBe('')
398
+ })
399
+
400
+ it('returns empty string when value is empty array', () => {
401
+ const checkboxField = field as CheckboxesField
402
+ const result = checkboxField.getDisplayStringFromFormValue([])
403
+ expect(result).toBe('')
404
+ })
405
+
406
+ it.each([...options.examples])(
407
+ 'returns text for single selected value',
408
+ (item) => {
409
+ const checkboxField = field as CheckboxesField
410
+ const result = checkboxField.getDisplayStringFromFormValue([
411
+ item.value
412
+ ])
413
+ expect(result).toBe(item.text)
414
+ }
415
+ )
416
+
417
+ it('returns comma-separated text for multiple selected values', () => {
418
+ const checkboxField = field as CheckboxesField
419
+ const item1 = options.examples[0]
420
+ const item2 = options.examples[2]
421
+
422
+ const result = checkboxField.getDisplayStringFromFormValue([
423
+ item1.value,
424
+ item2.value
425
+ ])
426
+
427
+ expect(result).toBe(`${item1.text}, ${item2.text}`)
428
+ })
429
+ })
410
430
  })
411
431
  })
@@ -65,9 +65,15 @@ export class CheckboxesField extends SelectionControlField {
65
65
  return this.isValue(value) ? value : undefined
66
66
  }
67
67
 
68
- getDisplayStringFromFormValue(selected: (string | number | boolean)[]) {
68
+ getDisplayStringFromFormValue(
69
+ selected: (string | number | boolean)[] | undefined
70
+ ) {
69
71
  const { items } = this
70
72
 
73
+ if (!selected) {
74
+ return ''
75
+ }
76
+
71
77
  // Map selected values to text
72
78
  return items
73
79
  .filter((item) => selected.includes(item.value))