Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@
},
"license": "SEE LICENSE IN LICENSE",
"dependencies": {
"@defra/forms-model": "^3.0.644",
"@defra/forms-model": "^3.0.647",
"@defra/hapi-tracing": "^1.29.0",
"@defra/interactive-map": "^0.0.17-alpha",
"@elastic/ecs-pino-format": "^1.5.0",
Expand Down
57 changes: 56 additions & 1 deletion src/server/plugins/engine/components/CheckboxesField.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,61 @@ describe.each([
)
})

it('is configured with min/max items', () => {
const collectionLimited = new ComponentCollection(
[{ ...def, schema: { min: 2, max: 4 } }],
{ model }
)
const { formSchema } = collectionLimited
const { keys } = formSchema.describe()

expect(keys).toHaveProperty(
'myComponent',
expect.objectContaining({
items: [
{
allow: options.allow,
flags: {
label: def.shortDescription,
only: true
},
type: options.list.type
}
],
rules: [
{ args: { limit: 2 }, name: 'min' },
{ args: { limit: 4 }, name: 'max' }
]
})
)
})

it('is configured with length items', () => {
const collectionLimited = new ComponentCollection(
[{ ...def, schema: { length: 3 } }],
{ model }
)
const { formSchema } = collectionLimited
const { keys } = formSchema.describe()

expect(keys).toHaveProperty(
'myComponent',
expect.objectContaining({
items: [
{
allow: options.allow,
flags: {
label: def.shortDescription,
only: true
},
type: options.list.type
}
],
rules: [{ args: { limit: 3 }, name: 'length' }]
})
)
})

it('adds errors for empty value', () => {
const result = collection.validate(getFormData())

Expand Down Expand Up @@ -386,7 +441,7 @@ describe.each([
it('should return errors', () => {
const errors = field.getAllPossibleErrors()
expect(errors.baseErrors).not.toBeEmpty()
expect(errors.advancedSettingsErrors).toBeEmpty()
expect(errors.advancedSettingsErrors).not.toBeEmpty()
})
})

Expand Down
40 changes: 40 additions & 0 deletions src/server/plugins/engine/components/CheckboxesField.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,17 @@ import { isFormValue } from '~/src/server/plugins/engine/components/FormComponen
import { SelectionControlField } from '~/src/server/plugins/engine/components/SelectionControlField.js'
import { type FormModel } from '~/src/server/plugins/engine/models/FormModel.js'
import { type QuestionPageController } from '~/src/server/plugins/engine/pageControllers/QuestionPageController.js'
import { messageTemplate } from '~/src/server/plugins/engine/pageControllers/validationOptions.js'
import {
type ErrorMessageTemplateList,
type FormState,
type FormStateValue,
type FormSubmissionState
} from '~/src/server/plugins/engine/types.js'

export class CheckboxesField extends SelectionControlField {
declare options: CheckboxesFieldComponent['options']
declare schema: CheckboxesFieldComponent['schema']
declare formSchema: ArraySchema<string> | ArraySchema<number>
declare stateSchema: ArraySchema<string> | ArraySchema<number>

Expand All @@ -24,6 +27,7 @@ export class CheckboxesField extends SelectionControlField {

const { listType: type } = this
const { options } = def
const schema = 'schema' in def ? def.schema : {}

let formSchema =
type === 'string' ? joi.array<string>() : joi.array<number>()
Expand All @@ -42,6 +46,18 @@ export class CheckboxesField extends SelectionControlField {
formSchema = formSchema.optional()
}

if (typeof schema?.length === 'number') {
formSchema = formSchema.length(schema.length)
} else {
if (typeof schema?.min === 'number') {
formSchema = formSchema.min(schema.min)
}

if (typeof schema?.max === 'number') {
formSchema = formSchema.max(schema.max)
}
}

this.formSchema = formSchema.default([])
this.stateSchema = formSchema.default(null).allow(null)
this.options = options
Expand Down Expand Up @@ -112,6 +128,30 @@ export class CheckboxesField extends SelectionControlField {
return this.getContextValueFromFormValue(values)
}

/**
* For error preview page that shows all possible errors on a component
*/
getAllPossibleErrors(): ErrorMessageTemplateList {
return CheckboxesField.getAllPossibleErrors()
}

/**
* Static version of getAllPossibleErrors that doesn't require a component instance.
*/
static getAllPossibleErrors(): ErrorMessageTemplateList {
const parentErrors = SelectionControlField.getAllPossibleErrors()

return {
...parentErrors,
advancedSettingsErrors: [
...parentErrors.advancedSettingsErrors,
{ type: 'array.min', template: messageTemplate.arrayMin },
{ type: 'array.max', template: messageTemplate.arrayMax },
{ type: 'array.length', template: messageTemplate.arrayLength }
]
}
}

isValue(value?: FormStateValue | FormState): value is Item['value'][] {
if (!Array.isArray(value)) {
return false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,10 @@ export const messageTemplate: Record<string, JoiExpression> = {
) as JoiExpression,
dateFormat: '{{#title}} must be a real date',
dateMin: '{{#title}} must be the same as or after {{#limit}}',
dateMax: '{{#title}} must be the same as or before {{#limit}}'
dateMax: '{{#title}} must be the same as or before {{#limit}}',
arrayMax: '{{#label}} must contain less than or equal to {{#limit}} items',
arrayMin: '{{#label}} must contain at least {{#limit}} items',
arrayLength: '{{#label}} must contain {{#limit}} items'
Comment thread
alexluckett marked this conversation as resolved.
Outdated
}

export const messages: LanguageMessagesExt = {
Expand Down
Loading