@defra/forms-model 3.0.458 → 3.0.459

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.
@@ -47,9 +47,9 @@
47
47
  "properties": {
48
48
  "id": {
49
49
  "type": "string",
50
+ "description": "Unique identifier for the list item",
50
51
  "format": "uuid",
51
- "title": "Id",
52
- "description": "The id value."
52
+ "title": "Id"
53
53
  },
54
54
  "text": {
55
55
  "type": "string",
@@ -286,9 +286,9 @@
286
286
  "properties": {
287
287
  "id": {
288
288
  "type": "string",
289
+ "description": "Unique identifier for the list item",
289
290
  "format": "uuid",
290
- "title": "Id",
291
- "description": "The id value."
291
+ "title": "Id"
292
292
  },
293
293
  "text": {
294
294
  "type": "string",
@@ -83,11 +83,6 @@
83
83
  ],
84
84
  "additionalProperties": false
85
85
  },
86
- "condition": {
87
- "type": "string",
88
- "description": "Optional condition that determines if this page is shown",
89
- "title": "Condition"
90
- },
91
86
  "next": {
92
87
  "type": "array",
93
88
  "description": "Possible navigation paths after this page",
@@ -368,16 +363,24 @@
368
363
  "additionalProperties": true,
369
364
  "title": "Schema"
370
365
  },
371
- "list": {
372
- "type": "string",
373
- "description": "Reference to a predefined list of options for select components",
374
- "title": "List"
375
- },
376
366
  "id": {
377
367
  "type": "string",
378
368
  "description": "Unique identifier for the component",
379
369
  "format": "uuid",
380
370
  "title": "Id"
371
+ },
372
+ "list": {
373
+ "description": "List id reference to a predefined list of options for select components",
374
+ "const": {
375
+ "ref": {
376
+ "path": [
377
+ "lists"
378
+ ],
379
+ "ancestor": "root",
380
+ "in": true
381
+ }
382
+ },
383
+ "title": "List"
381
384
  }
382
385
  },
383
386
  "required": [
@@ -387,6 +390,19 @@
387
390
  "title": "Components Item"
388
391
  },
389
392
  "title": "Components"
393
+ },
394
+ "condition": {
395
+ "description": "Optional condition that determines if this page is shown",
396
+ "const": {
397
+ "ref": {
398
+ "path": [
399
+ "conditions"
400
+ ],
401
+ "ancestor": "root",
402
+ "in": true
403
+ }
404
+ },
405
+ "title": "Condition"
390
406
  }
391
407
  },
392
408
  "required": [
@@ -1,6 +1,8 @@
1
1
  export enum ConditionType {
2
2
  Value = 'Value',
3
- RelativeDate = 'RelativeDate'
3
+ RelativeDate = 'RelativeDate',
4
+ StringValue = 'StringValue',
5
+ ListItemRef = 'ListItemRef'
4
6
  }
5
7
 
6
8
  export enum Coordinator {
@@ -17,6 +17,17 @@ export interface ConditionValueData {
17
17
  display: string
18
18
  }
19
19
 
20
+ export interface Condition2StringValueData {
21
+ type: ConditionType.StringValue
22
+ value: string
23
+ }
24
+
25
+ export interface Condition2ListItemRefValueData {
26
+ type: ConditionType.ListItemRef
27
+ listId: string
28
+ itemId: string
29
+ }
30
+
20
31
  export interface RelativeDateValueData {
21
32
  type: ConditionType.RelativeDate
22
33
  period: string
@@ -37,16 +48,33 @@ export interface ConditionData {
37
48
  coordinator?: Coordinator
38
49
  }
39
50
 
51
+ export interface Condition2Data {
52
+ id: string
53
+ componentId: string
54
+ operator: OperatorName
55
+ value:
56
+ | Condition2ListItemRefValueData
57
+ | Condition2StringValueData
58
+ | RelativeDateValueData
59
+ }
60
+
40
61
  export interface ConditionRefData {
41
62
  conditionName: string
42
63
  conditionDisplayName: string
43
64
  coordinator?: Coordinator
44
65
  }
45
66
 
67
+ export interface Condition2RefData {
68
+ id: string
69
+ conditionId: string
70
+ }
71
+
46
72
  export interface ConditionGroupData {
47
73
  conditions: (ConditionData | ConditionRefData | ConditionGroupData)[]
48
74
  }
49
75
 
76
+ export type Condition2GroupData = (Condition2Data | Condition2RefData)[]
77
+
50
78
  export interface ConditionsModelData extends ConditionGroupData {
51
79
  name: string
52
80
  }
@@ -4,6 +4,11 @@ import { v4 as uuidV4 } from 'uuid'
4
4
  import { ComponentType } from '~/src/components/enums.js'
5
5
  import { type ComponentDef } from '~/src/components/types.js'
6
6
  import {
7
+ type Condition2Data,
8
+ type Condition2GroupData,
9
+ type Condition2ListItemRefValueData,
10
+ type Condition2RefData,
11
+ type Condition2StringValueData,
7
12
  type ConditionData,
8
13
  type ConditionFieldData,
9
14
  type ConditionGroupData,
@@ -13,6 +18,7 @@ import {
13
18
  type RelativeDateValueData
14
19
  } from '~/src/conditions/types.js'
15
20
  import {
21
+ type Condition2Wrapper,
16
22
  type ConditionWrapper,
17
23
  type Event,
18
24
  type EventOptions,
@@ -28,6 +34,43 @@ import {
28
34
  type RepeatSchema,
29
35
  type Section
30
36
  } from '~/src/form/form-definition/types.js'
37
+ import { hasComponents } from '~/src/pages/helpers.js'
38
+
39
+ const idSchemaOptional = Joi.string().uuid()
40
+
41
+ const idSchema = idSchemaOptional.default(() => uuidV4())
42
+
43
+ const conditionIdRef = Joi.ref('/conditions', {
44
+ in: true,
45
+ adjust: (conditions: Condition2Wrapper[]) =>
46
+ conditions.map((condition) => condition.name)
47
+ })
48
+
49
+ const componentIdRefSchema = Joi.ref('/pages', {
50
+ in: true,
51
+ adjust: (pages: Page[]) =>
52
+ pages.flatMap((page) =>
53
+ hasComponents(page)
54
+ ? page.components
55
+ .filter((component) => component.id)
56
+ .map((component) => component.id)
57
+ : []
58
+ )
59
+ })
60
+
61
+ const listIdRef = Joi.ref('/lists', {
62
+ in: true,
63
+ adjust: (lists: List[]) =>
64
+ lists.filter((list) => list.id).map((list) => list.id)
65
+ })
66
+
67
+ const listItemIdRef = Joi.ref('/lists', {
68
+ in: true,
69
+ adjust: (lists: List[]) =>
70
+ lists.flatMap((list) =>
71
+ list.items.filter((item) => item.id).map((item) => item.id)
72
+ )
73
+ })
31
74
 
32
75
  const sectionsSchema = Joi.object<Section>()
33
76
  .description('A form section grouping related pages together')
@@ -84,13 +127,49 @@ const conditionValueSchema = Joi.object<ConditionValueData>()
84
127
  .description('Human-readable version of the value for display purposes')
85
128
  })
86
129
 
87
- const relativeDateValueSchema = Joi.object<RelativeDateValueData>()
130
+ const condition2StringValueDataSchema = Joi.object<Condition2StringValueData>()
131
+ .description('String value specification for a condition')
132
+ .keys({
133
+ type: Joi.string()
134
+ .trim()
135
+ .valid('StringValue')
136
+ .required()
137
+ .description('Type of the condition value, should be "StringValue"'),
138
+ value: Joi.string()
139
+ .trim()
140
+ .required()
141
+ .description('The actual value to compare against')
142
+ })
143
+
144
+ const condition2ListItemRefDataSchema =
145
+ Joi.object<Condition2ListItemRefValueData>()
146
+ .description('List item ref specification for a condition')
147
+ .keys({
148
+ type: Joi.string()
149
+ .trim()
150
+ .valid('ListItemRef')
151
+ .required()
152
+ .description('Type of the condition value, should be "ListItemRef"'),
153
+ listId: Joi.string()
154
+ .valid(listIdRef)
155
+ .trim()
156
+ .required()
157
+ .description('The id of the list'),
158
+ itemId: Joi.string()
159
+ .trim()
160
+ .valid(listItemIdRef)
161
+ .required()
162
+ .description('The id of the list item')
163
+ })
164
+
165
+ const relativeDateValueDataSchema = Joi.object<RelativeDateValueData>()
88
166
  .description('Relative date specification for date-based conditions')
89
167
  .keys({
90
168
  type: Joi.string()
91
169
  .trim()
170
+ .valid('RelativeDate')
92
171
  .required()
93
- .description('Data type identifier, should be "RelativeDate"'),
172
+ .description('Type of the condition value, should be "RelativeDate"'),
94
173
  period: Joi.string()
95
174
  .trim()
96
175
  .required()
@@ -98,7 +177,7 @@ const relativeDateValueSchema = Joi.object<RelativeDateValueData>()
98
177
  unit: Joi.string()
99
178
  .trim()
100
179
  .required()
101
- .description('Time unit (e.g., days, weeks, months, years)'),
180
+ .description('Time unit (e.g. days, weeks, months, years)'),
102
181
  direction: Joi.string()
103
182
  .trim()
104
183
  .required()
@@ -124,6 +203,17 @@ const conditionRefSchema = Joi.object<ConditionRefData>()
124
203
  )
125
204
  })
126
205
 
206
+ const condition2RefDataSchema = Joi.object<Condition2RefData>()
207
+ .description('Reference to a named condition defined elsewhere')
208
+ .keys({
209
+ id: idSchema.description('Unique identifier for the referenced condition'),
210
+ conditionId: Joi.string()
211
+ .trim()
212
+ .required()
213
+ .valid(conditionIdRef)
214
+ .description('Name of the referenced condition')
215
+ })
216
+
127
217
  const conditionSchema = Joi.object<ConditionData>()
128
218
  .description('Condition definition specifying a logical comparison')
129
219
  .keys({
@@ -135,7 +225,7 @@ const conditionSchema = Joi.object<ConditionData>()
135
225
  .required()
136
226
  .description('Comparison operator (equals, greaterThan, contains, etc.)'),
137
227
  value: Joi.alternatives()
138
- .try(conditionValueSchema, relativeDateValueSchema)
228
+ .try(conditionValueSchema, relativeDateValueDataSchema)
139
229
  .description(
140
230
  'Value to compare the field against, either fixed or relative date'
141
231
  ),
@@ -147,6 +237,34 @@ const conditionSchema = Joi.object<ConditionData>()
147
237
  )
148
238
  })
149
239
 
240
+ const condition2DataSchema = Joi.object<Condition2Data>()
241
+ .description('Condition definition')
242
+ .keys({
243
+ id: idSchema.description(
244
+ 'Unique identifier used to reference this condition'
245
+ ),
246
+ componentId: Joi.string()
247
+ .trim()
248
+ .valid(componentIdRefSchema)
249
+ .required()
250
+ .description(
251
+ 'Reference to the component id being evaluated in this condition'
252
+ ),
253
+ operator: Joi.string()
254
+ .trim()
255
+ .required()
256
+ .description('Comparison operator (equals, greaterThan, contains, etc.)'),
257
+ value: Joi.alternatives()
258
+ .try(
259
+ condition2StringValueDataSchema,
260
+ condition2ListItemRefDataSchema,
261
+ relativeDateValueDataSchema
262
+ )
263
+ .description(
264
+ 'Value to compare the field against, either fixed or relative date'
265
+ )
266
+ })
267
+
150
268
  const conditionGroupSchema = Joi.object<ConditionGroupData>()
151
269
  .description('Group of conditions combined with logical operators')
152
270
  .keys({
@@ -197,14 +315,32 @@ const conditionWrapperSchema = Joi.object<ConditionWrapper>()
197
315
  .description('The complete condition definition')
198
316
  })
199
317
 
200
- export const componentSchema = Joi.object<ComponentDef>()
201
- .description('Form component definition specifying UI element behavior')
318
+ export const conditionWrapperSchemaV2 = Joi.object<Condition2Wrapper>()
319
+ .description('Container for a named condition with its definition')
202
320
  .keys({
203
- id: Joi.string()
321
+ name: Joi.string()
322
+ .trim()
323
+ .description('Unique identifier for the condition'),
324
+ displayName: Joi.string()
204
325
  .trim()
205
- .uuid()
326
+ .description('Human-readable name for display in the UI'),
327
+ coordinator: Joi.string()
206
328
  .optional()
207
- .description('Unique identifier for the component'),
329
+ .description(
330
+ 'Logical operator connecting this condition with others (AND, OR)'
331
+ ),
332
+ conditions: Joi.array<Condition2GroupData>()
333
+ .items(
334
+ Joi.alternatives().try(condition2DataSchema, condition2RefDataSchema)
335
+ )
336
+ .description('Array of conditions or condition references')
337
+ })
338
+ .description('Condition schema for V2 forms')
339
+
340
+ export const componentSchema = Joi.object<ComponentDef>()
341
+ .description('Form component definition specifying UI element behavior')
342
+ .keys({
343
+ id: idSchemaOptional.description('Unique identifier for the component'),
208
344
  type: Joi.string<ComponentType>()
209
345
  .trim()
210
346
  .required()
@@ -303,11 +439,13 @@ export const componentSchema = Joi.object<ComponentDef>()
303
439
 
304
440
  export const componentSchemaV2 = componentSchema
305
441
  .keys({
306
- id: Joi.string()
307
- .trim()
308
- .uuid()
309
- .default(() => uuidV4())
310
- .description('Unique identifier for the component')
442
+ id: idSchema.description('Unique identifier for the component'),
443
+ list: Joi.string()
444
+ .valid(listIdRef)
445
+ .optional()
446
+ .description(
447
+ 'List id reference to a predefined list of options for select components'
448
+ )
311
449
  })
312
450
  .description('Component schema for V2 forms')
313
451
 
@@ -418,11 +556,7 @@ const eventsSchema = Joi.object<Events>()
418
556
  export const pageSchema = Joi.object<Page>()
419
557
  .description('Form page definition specifying content and behavior')
420
558
  .keys({
421
- id: Joi.string()
422
- .trim()
423
- .uuid()
424
- .optional()
425
- .description('Unique identifier for the page'),
559
+ id: idSchemaOptional.description('Unique identifier for the page'),
426
560
  path: Joi.string()
427
561
  .trim()
428
562
  .required()
@@ -477,11 +611,7 @@ export const pageSchema = Joi.object<Page>()
477
611
  */
478
612
  export const pageSchemaV2 = pageSchema
479
613
  .append({
480
- id: Joi.string()
481
- .trim()
482
- .uuid()
483
- .default(() => uuidV4())
484
- .description('Unique identifier for the page'),
614
+ id: idSchema.description('Unique identifier for the page'),
485
615
  title: Joi.string()
486
616
  .trim()
487
617
  .allow('')
@@ -493,17 +623,19 @@ export const pageSchemaV2 = pageSchema
493
623
  .items(componentSchemaV2)
494
624
  .unique('name')
495
625
  .unique('id')
496
- .description('Components schema for V2 forms')
626
+ .description('Components schema for V2 forms'),
627
+ condition: Joi.string()
628
+ .trim()
629
+ .valid(conditionIdRef)
630
+ .optional()
631
+ .description('Optional condition that determines if this page is shown')
497
632
  })
498
633
  .description('Page schema for V2 forms')
499
634
 
500
635
  const baseListItemSchema = Joi.object<Item>()
501
636
  .description('Base schema for list items with common properties')
502
637
  .keys({
503
- id: Joi.string()
504
- .trim()
505
- .uuid()
506
- .default(() => uuidV4()),
638
+ id: idSchema.description('Unique identifier for the list item'),
507
639
  text: Joi.string()
508
640
  .trim()
509
641
  .allow('')
@@ -531,10 +663,7 @@ const baseListItemSchema = Joi.object<Item>()
531
663
  hint: Joi.object<Item['hint']>()
532
664
  .optional()
533
665
  .keys({
534
- id: Joi.string()
535
- .trim()
536
- .uuid()
537
- .default(() => uuidV4()),
666
+ id: idSchema,
538
667
  text: Joi.string().trim()
539
668
  })
540
669
  .description('Optional hint text to be shown on list item')
@@ -560,11 +689,7 @@ const numberListItemSchema = baseListItemSchema
560
689
  export const listSchema = Joi.object<List>()
561
690
  .description('Reusable list of options for select components')
562
691
  .keys({
563
- id: Joi.string()
564
- .trim()
565
- .uuid()
566
- .optional()
567
- .description('Unique identifier for the list'),
692
+ id: idSchemaOptional.description('Unique identifier for the list'),
568
693
  name: Joi.string()
569
694
  .trim()
570
695
  .required()
@@ -602,11 +727,7 @@ export const listSchema = Joi.object<List>()
602
727
  */
603
728
  export const listSchemaV2 = listSchema
604
729
  .keys({
605
- id: Joi.string()
606
- .trim()
607
- .uuid()
608
- .default(() => uuidV4())
609
- .description('Unique identifier for the list')
730
+ id: idSchema.description('Unique identifier for the list')
610
731
  })
611
732
  .description('List schema for V2 forms')
612
733
 
@@ -746,7 +867,12 @@ export const formDefinitionV2Schema = formDefinitionSchema
746
867
  .unique('name')
747
868
  .unique('title')
748
869
  .unique('id')
749
- .description('Lists schema for V2 forms')
870
+ .description('Lists schema for V2 forms'),
871
+ conditions: Joi.array<Condition2Wrapper>()
872
+ .items(conditionWrapperSchemaV2)
873
+ .unique('name')
874
+ .unique('displayName')
875
+ .description('Named conditions used for form logic')
750
876
  })
751
877
  .description('Form definition schema for V2')
752
878
 
@@ -1,6 +1,9 @@
1
1
  import { type ComponentDef } from '~/src/components/types.js'
2
- import { type ConditionsModelData } from '~/src/conditions/types.js'
3
- import { formDefinitionSchema } from '~/src/form/form-definition/index.js'
2
+ import { type Coordinator } from '~/src/conditions/enums.js'
3
+ import {
4
+ type Condition2GroupData,
5
+ type ConditionsModelData
6
+ } from '~/src/conditions/types.js'
4
7
  import { type ControllerPath, type ControllerType } from '~/src/pages/enums.js'
5
8
 
6
9
  export enum Engine {
@@ -159,14 +162,20 @@ export interface ConditionWrapper {
159
162
  value: ConditionsModelData
160
163
  }
161
164
 
165
+ export interface Condition2Wrapper {
166
+ name: string
167
+ displayName: string
168
+ coordinator?: Coordinator
169
+ conditions: Condition2GroupData
170
+ }
171
+
162
172
  /**
163
173
  * Interface for `formDefinitionSchema` Joi schema
164
- * @see {@link formDefinitionSchema}
165
174
  */
166
175
  export interface FormDefinition {
167
176
  engine?: Engine
168
177
  pages: Page[]
169
- conditions: ConditionWrapper[]
178
+ conditions: (ConditionWrapper | Condition2Wrapper)[]
170
179
  lists: List[]
171
180
  sections: Section[]
172
181
  startPage?: string