@digigov/form 2.0.10 → 2.0.11

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/CHANGELOG.md CHANGED
@@ -1,9 +1,17 @@
1
1
  # Change Log - @digigov/form
2
2
 
3
- <!-- This log was last generated on Thu, 05 Mar 2026 17:08:26 GMT and should not be manually modified. -->
3
+ <!-- This log was last generated on Mon, 09 Mar 2026 12:14:45 GMT and should not be manually modified. -->
4
4
 
5
5
  <!-- Start content -->
6
6
 
7
+ ## 2.0.11
8
+
9
+ Mon, 09 Mar 2026 12:14:45 GMT
10
+
11
+ ### Patches
12
+
13
+ - feat(form): button disable conditional should depend on internal array values (cpapakon@admin.grnet.gr)
14
+
7
15
  ## 2.0.10
8
16
 
9
17
  Thu, 05 Mar 2026 17:08:26 GMT
@@ -193,7 +193,7 @@ const FormDialog = /*#__PURE__*/ react.forwardRef(({ name: fieldArrayName, contr
193
193
  color: "secondary",
194
194
  variant: customField.extra?.label.object?.addButtonVariant,
195
195
  onClick: handleAddClick,
196
- disabled: customField?.editable === false || customField.extra?.editVariant === 'noinput' || fields.length >= (customField.extra?.max ?? Number.MAX_VALUE)
196
+ disabled: customField?.editable === false || customField.extra?.editVariant === 'noinput' || fieldArrayValues?.length >= (customField.extra?.max ?? Number.MAX_VALUE)
197
197
  }, customField.extra?.label.object?.add)));
198
198
  });
199
199
  FormDialog.displayName = 'FormDialog';
package/index.js CHANGED
@@ -1,4 +1,4 @@
1
- /** @license Digigov v2.0.10
1
+ /** @license Digigov v2.0.11
2
2
  *
3
3
  * This source code is licensed under the BSD-2-Clause license found in the
4
4
  * LICENSE file in the root directory of this source tree.
package/lazy.d.ts CHANGED
@@ -8,12 +8,12 @@ declare const _default: {
8
8
  FieldBase: import("react").LazyExoticComponent<import("react").FC<import("./internal.js").FieldBaseProps>>;
9
9
  FieldBaseContainer: import("react").LazyExoticComponent<import("react").ForwardRefExoticComponent<import("./internal.js").FieldContainerProps & import("react").RefAttributes<HTMLFieldSetElement>>>;
10
10
  FieldConditional: import("react").LazyExoticComponent<import("react").FC<import("./internal.js").FieldConditionalProps>>;
11
- FieldObject: import("react").LazyExoticComponent<import("react").FC<import("./FieldObject/index.js").FieldObjectProps>>;
12
11
  BaseFieldArray: import("react").LazyExoticComponent<import("react").ForwardRefExoticComponent<Omit<import("./FieldArray/index.js").FieldArrayProps, "reset" | "resetField" | "setValue" | "clearErrors" | "trigger" | "registerFieldFocus"> & {
13
12
  deleteConfirmation?: boolean;
14
13
  } & import("react").RefAttributes<HTMLButtonElement>>>;
15
14
  DeleteConfirmationModal: import("react").LazyExoticComponent<({ remove, close, ...props }: import("./FieldArray/DeleteConfirmationModal.js").DeleteConfirmationModalProps) => import("react").JSX.Element>;
16
15
  FieldArray: import("react").LazyExoticComponent<import("react").FC<import("./FieldArray/index.js").FieldArrayProps>>;
16
+ FieldObject: import("react").LazyExoticComponent<import("react").FC<import("./FieldObject/index.js").FieldObjectProps>>;
17
17
  FieldsetWithContext: import("react").LazyExoticComponent<import("react").FC<{
18
18
  name: string;
19
19
  }>>;
package/lazy.js CHANGED
@@ -27,9 +27,6 @@ const src_lazy = {
27
27
  FieldConditional: lazy(()=>import("./Field/FieldConditional.js").then((module)=>({
28
28
  default: module['FieldConditional']
29
29
  }))),
30
- FieldObject: lazy(()=>import("./FieldObject/index.js").then((module)=>({
31
- default: module['FieldObject']
32
- }))),
33
30
  BaseFieldArray: lazy(()=>import("./FieldArray/BaseFieldArray.js").then((module)=>({
34
31
  default: module['BaseFieldArray']
35
32
  }))),
@@ -39,6 +36,9 @@ const src_lazy = {
39
36
  FieldArray: lazy(()=>import("./FieldArray/index.js").then((module)=>({
40
37
  default: module['FieldArray']
41
38
  }))),
39
+ FieldObject: lazy(()=>import("./FieldObject/index.js").then((module)=>({
40
+ default: module['FieldObject']
41
+ }))),
42
42
  FieldsetWithContext: lazy(()=>import("./Fieldset/FieldsetWithContext.js").then((module)=>({
43
43
  default: module['FieldsetWithContext']
44
44
  }))),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@digigov/form",
3
- "version": "2.0.10",
3
+ "version": "2.0.11",
4
4
  "description": "@digigov form builder",
5
5
  "author": "GRNET Developers <devs@lists.grnet.gr>",
6
6
  "license": "BSD-2-Clause",
@@ -15,14 +15,14 @@
15
15
  "yup": "0.32.11",
16
16
  "dayjs": "1.10.4",
17
17
  "@hookform/resolvers": "5.0.1",
18
- "@digigov/react-icons": "2.0.10"
18
+ "@digigov/react-icons": "2.0.11"
19
19
  },
20
20
  "peerDependencies": {
21
21
  "clsx": "2.1.1",
22
22
  "react": "^18.3.0 || ^19.1.0",
23
23
  "react-dom": "^18.3.0 || ^19.1.0",
24
- "@digigov/ui": "2.0.10",
25
- "@digigov/react-core": "2.0.10"
24
+ "@digigov/ui": "2.0.11",
25
+ "@digigov/react-core": "2.0.11"
26
26
  },
27
27
  "gitHead": "c903a46306f77f55ad7fc4d2e274006f39a6c871",
28
28
  "private": false,
package/registry.d.ts CHANGED
@@ -10,10 +10,10 @@ declare const _default: {
10
10
  '@digigov/form/Field/FieldConditional': {};
11
11
  '@digigov/form/Field': {};
12
12
  '@digigov/form/Field/types': {};
13
- '@digigov/form/FieldObject': {};
14
13
  '@digigov/form/FieldArray/BaseFieldArray': {};
15
14
  '@digigov/form/FieldArray/DeleteConfirmationModal': {};
16
15
  '@digigov/form/FieldArray': {};
16
+ '@digigov/form/FieldObject': {};
17
17
  '@digigov/form/Fieldset/FieldsetWithContext': {};
18
18
  '@digigov/form/Fieldset': {};
19
19
  '@digigov/form/Fieldset/types': {};
@@ -49,9 +49,9 @@ declare const _default: {
49
49
  '@digigov/form/Questions/Step': {};
50
50
  '@digigov/form/Questions/Step/types': {};
51
51
  '@digigov/form/inputs/AutoCompleteInput': {};
52
+ '@digigov/form/inputs/DateTimeInput': {};
52
53
  '@digigov/form/inputs/Checkboxes': {};
53
54
  '@digigov/form/inputs/DateInput': {};
54
- '@digigov/form/inputs/DateTimeInput': {};
55
55
  '@digigov/form/inputs/FileInput': {};
56
56
  '@digigov/form/inputs/ImageInput': {};
57
57
  '@digigov/form/inputs/Input': {};
package/registry.js CHANGED
@@ -11,10 +11,10 @@ import * as __WEBPACK_EXTERNAL_MODULE__Field_FieldBase_js_c624a2e3__ from "./Fie
11
11
  import * as __WEBPACK_EXTERNAL_MODULE__Field_FieldBaseContainer_js_01a670a9__ from "./Field/FieldBaseContainer.js";
12
12
  import * as __WEBPACK_EXTERNAL_MODULE__Field_FieldConditional_js_198c9ee9__ from "./Field/FieldConditional.js";
13
13
  import * as __WEBPACK_EXTERNAL_MODULE__Field_types_js_6ef04275__ from "./Field/types.js";
14
- import * as __WEBPACK_EXTERNAL_MODULE__FieldObject_index_js_3053c0b3__ from "./FieldObject/index.js";
15
14
  import * as __WEBPACK_EXTERNAL_MODULE__FieldArray_BaseFieldArray_js_51879b3c__ from "./FieldArray/BaseFieldArray.js";
16
15
  import * as __WEBPACK_EXTERNAL_MODULE__FieldArray_DeleteConfirmationModal_js_c12845d4__ from "./FieldArray/DeleteConfirmationModal.js";
17
16
  import * as __WEBPACK_EXTERNAL_MODULE__FieldArray_index_js_f6888d1e__ from "./FieldArray/index.js";
17
+ import * as __WEBPACK_EXTERNAL_MODULE__FieldObject_index_js_3053c0b3__ from "./FieldObject/index.js";
18
18
  import * as __WEBPACK_EXTERNAL_MODULE__Fieldset_FieldsetWithContext_js_aa40ade4__ from "./Fieldset/FieldsetWithContext.js";
19
19
  import * as __WEBPACK_EXTERNAL_MODULE__Fieldset_types_js_45030319__ from "./Fieldset/types.js";
20
20
  import * as __WEBPACK_EXTERNAL_MODULE__MultiplicityField_add_objects_js_bc5e12f3__ from "./MultiplicityField/add-objects.js";
@@ -48,9 +48,9 @@ import * as __WEBPACK_EXTERNAL_MODULE__Questions_Step_getAddMoreFields_js_08ef7e
48
48
  import * as __WEBPACK_EXTERNAL_MODULE__Questions_Step_index_js_a5d00e0e__ from "./Questions/Step/index.js";
49
49
  import * as __WEBPACK_EXTERNAL_MODULE__Questions_Step_types_js_adca4924__ from "./Questions/Step/types.js";
50
50
  import * as __WEBPACK_EXTERNAL_MODULE__inputs_AutoCompleteInput_index_js_64b911d2__ from "./inputs/AutoCompleteInput/index.js";
51
+ import * as __WEBPACK_EXTERNAL_MODULE__inputs_DateTimeInput_index_js_c8900e1c__ from "./inputs/DateTimeInput/index.js";
51
52
  import * as __WEBPACK_EXTERNAL_MODULE__inputs_Checkboxes_index_js_794dd192__ from "./inputs/Checkboxes/index.js";
52
53
  import * as __WEBPACK_EXTERNAL_MODULE__inputs_DateInput_index_js_4dd00af8__ from "./inputs/DateInput/index.js";
53
- import * as __WEBPACK_EXTERNAL_MODULE__inputs_DateTimeInput_index_js_c8900e1c__ from "./inputs/DateTimeInput/index.js";
54
54
  import * as __WEBPACK_EXTERNAL_MODULE__inputs_FileInput_index_js_a3efab43__ from "./inputs/FileInput/index.js";
55
55
  import * as __WEBPACK_EXTERNAL_MODULE__inputs_ImageInput_index_js_db0d371d__ from "./inputs/ImageInput/index.js";
56
56
  import * as __WEBPACK_EXTERNAL_MODULE__inputs_Input_index_js_a3d24895__ from "./inputs/Input/index.js";
@@ -99,10 +99,10 @@ const registry = {
99
99
  '@digigov/form/Field/FieldConditional': lazyImport(__WEBPACK_EXTERNAL_MODULE__Field_FieldConditional_js_198c9ee9__),
100
100
  '@digigov/form/Field': lazyImport(__WEBPACK_EXTERNAL_MODULE__Field_index_js_29296f3b__),
101
101
  '@digigov/form/Field/types': lazyImport(__WEBPACK_EXTERNAL_MODULE__Field_types_js_6ef04275__),
102
- '@digigov/form/FieldObject': lazyImport(__WEBPACK_EXTERNAL_MODULE__FieldObject_index_js_3053c0b3__),
103
102
  '@digigov/form/FieldArray/BaseFieldArray': lazyImport(__WEBPACK_EXTERNAL_MODULE__FieldArray_BaseFieldArray_js_51879b3c__),
104
103
  '@digigov/form/FieldArray/DeleteConfirmationModal': lazyImport(__WEBPACK_EXTERNAL_MODULE__FieldArray_DeleteConfirmationModal_js_c12845d4__),
105
104
  '@digigov/form/FieldArray': lazyImport(__WEBPACK_EXTERNAL_MODULE__FieldArray_index_js_f6888d1e__),
105
+ '@digigov/form/FieldObject': lazyImport(__WEBPACK_EXTERNAL_MODULE__FieldObject_index_js_3053c0b3__),
106
106
  '@digigov/form/Fieldset/FieldsetWithContext': lazyImport(__WEBPACK_EXTERNAL_MODULE__Fieldset_FieldsetWithContext_js_aa40ade4__),
107
107
  '@digigov/form/Fieldset': lazyImport(__WEBPACK_EXTERNAL_MODULE__Fieldset_index_js_3b7183e3__),
108
108
  '@digigov/form/Fieldset/types': lazyImport(__WEBPACK_EXTERNAL_MODULE__Fieldset_types_js_45030319__),
@@ -138,9 +138,9 @@ const registry = {
138
138
  '@digigov/form/Questions/Step': lazyImport(__WEBPACK_EXTERNAL_MODULE__Questions_Step_index_js_a5d00e0e__),
139
139
  '@digigov/form/Questions/Step/types': lazyImport(__WEBPACK_EXTERNAL_MODULE__Questions_Step_types_js_adca4924__),
140
140
  '@digigov/form/inputs/AutoCompleteInput': lazyImport(__WEBPACK_EXTERNAL_MODULE__inputs_AutoCompleteInput_index_js_64b911d2__),
141
+ '@digigov/form/inputs/DateTimeInput': lazyImport(__WEBPACK_EXTERNAL_MODULE__inputs_DateTimeInput_index_js_c8900e1c__),
141
142
  '@digigov/form/inputs/Checkboxes': lazyImport(__WEBPACK_EXTERNAL_MODULE__inputs_Checkboxes_index_js_794dd192__),
142
143
  '@digigov/form/inputs/DateInput': lazyImport(__WEBPACK_EXTERNAL_MODULE__inputs_DateInput_index_js_4dd00af8__),
143
- '@digigov/form/inputs/DateTimeInput': lazyImport(__WEBPACK_EXTERNAL_MODULE__inputs_DateTimeInput_index_js_c8900e1c__),
144
144
  '@digigov/form/inputs/FileInput': lazyImport(__WEBPACK_EXTERNAL_MODULE__inputs_FileInput_index_js_a3efab43__),
145
145
  '@digigov/form/inputs/ImageInput': lazyImport(__WEBPACK_EXTERNAL_MODULE__inputs_ImageInput_index_js_db0d371d__),
146
146
  '@digigov/form/inputs/Input': lazyImport(__WEBPACK_EXTERNAL_MODULE__inputs_Input_index_js_a3d24895__),
@@ -289,7 +289,8 @@ export const FormDialog = React.forwardRef<HTMLButtonElement, FormDialogProps>(
289
289
  disabled={
290
290
  customField?.editable === false ||
291
291
  customField.extra?.editVariant === 'noinput' ||
292
- fields.length >= (customField.extra?.max ?? Number.MAX_VALUE)
292
+ fieldArrayValues?.length >=
293
+ (customField.extra?.max ?? Number.MAX_VALUE)
293
294
  }
294
295
  >
295
296
  {customField.extra?.label.object?.add}
@@ -272,12 +272,8 @@ describe('nested_fieldset_multiplicity case from dilosi', () => {
272
272
  it(`should open the nested_multiplicity_${fieldsetType} dialog on nested "add" button click`, async () => {
273
273
  render(<TestForm />);
274
274
  openMainDialog();
275
- const nestedFieldset = getNestedFieldset(fieldsetType);
276
- const nestedFieldsetAddButton = within(nestedFieldset!).getByRole(
277
- 'button',
278
- { name: 'Προσθήκη' }
279
- );
280
- fireEvent.click(nestedFieldsetAddButton);
275
+ const nestedAddButton = getNestedAddButton(fieldsetType);
276
+ fireEvent.click(nestedAddButton);
281
277
  expect(screen.getAllByRole('dialog').length).toBe(2);
282
278
  const inputField = screen.getByRole('textbox');
283
279
  expect(inputField).toBeInTheDocument();
@@ -306,18 +302,12 @@ describe('nested_fieldset_multiplicity case from dilosi', () => {
306
302
  it('should validate nested_multiplicity_required string field', async () => {
307
303
  render(<TestForm />);
308
304
  openMainDialog();
309
- const nestedFieldset = getNestedFieldset('required');
310
- const nestedFieldsetAddButton = within(nestedFieldset).getByRole('button', {
311
- name: 'Προσθήκη',
312
- });
313
- fireEvent.click(nestedFieldsetAddButton);
305
+ const nestedAddButton = getNestedAddButton('required');
306
+ fireEvent.click(nestedAddButton);
314
307
  expect(screen.getAllByRole('dialog').length).toBe(2);
315
308
  const inputField = screen.getByRole('textbox');
316
309
  expect(inputField).toHaveProperty('name', `nested_multiplicity_required`);
317
- const submitButton = screen.getAllByRole('button', {
318
- name: 'Αποθήκευση',
319
- })[1];
320
- fireEvent.click(submitButton);
310
+ clickNestedSaveButton();
321
311
  await waitFor(() =>
322
312
  expect(
323
313
  screen.getByText('Το πεδίο είναι υποχρεωτικό.')
@@ -328,49 +318,17 @@ describe('nested_fieldset_multiplicity case from dilosi', () => {
328
318
  it('should submit nested_multiplicity_required item added by nested form', async () => {
329
319
  render(<TestForm />);
330
320
  openMainDialog();
331
- const nestedFieldset = getNestedFieldset('required');
332
- const nestedFieldsetAddButton = within(nestedFieldset).getByRole('button', {
333
- name: 'Προσθήκη',
334
- });
335
- fireEvent.click(nestedFieldsetAddButton);
336
- expect(screen.getAllByRole('dialog').length).toBe(2);
337
- const nestedFieldsetInput = screen.getByRole('textbox');
338
- expect(nestedFieldsetInput).toBeInTheDocument();
339
- expect(nestedFieldsetInput).toHaveProperty(
340
- 'name',
341
- `nested_multiplicity_required`
342
- );
343
- fireEvent.change(nestedFieldsetInput, {
344
- target: { value: 'Test Value' },
345
- });
346
- expect(nestedFieldsetInput).toHaveValue('Test Value');
347
- // There are two "Αποθήκευση" buttons, one for the nested dialog and one for the main dialog
348
- const submitButton = screen.getAllByRole('button', {
349
- name: 'Αποθήκευση',
350
- })[1];
351
- fireEvent.click(submitButton);
352
- await waitFor(() => {
353
- // Only one dialog should remain open (the main one)
354
- expect(screen.getAllByRole('dialog').length).toBe(1);
355
- });
321
+
322
+ const nestedAddButton = getNestedAddButton('required');
323
+ await addNestedItem(nestedAddButton, 'Test Value');
356
324
 
357
325
  expect(screen.getByText('Test Value')).toBeInTheDocument();
358
326
  expect(submitHandler).not.toHaveBeenCalled();
359
327
 
360
- const mainDialogSubmitButton = screen.getByRole('button', {
361
- name: 'Αποθήκευση',
362
- });
363
- fireEvent.click(mainDialogSubmitButton);
364
- await waitFor(() => {
365
- expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
366
- });
328
+ await saveMainDialog();
367
329
  expect(screen.getByText('Test Value')).toBeInTheDocument();
368
- const continueButton = screen.getByRole('button', { name: 'Συνέχεια' });
369
- expect(continueButton).toBeInTheDocument();
370
- fireEvent.click(continueButton);
371
- await waitFor(() => {
372
- expect(submitHandler).toHaveBeenCalledTimes(1);
373
- });
330
+
331
+ await submitForm(submitHandler);
374
332
  expect(submitHandler).toHaveBeenCalledWith({
375
333
  nested_fieldset_multiplicity: expect.arrayContaining([
376
334
  {
@@ -386,108 +344,25 @@ describe('nested_fieldset_multiplicity case from dilosi', () => {
386
344
  openMainDialog();
387
345
 
388
346
  // Add multiple nested_multiplicity_required items
389
- const requiredNestedFieldset = getNestedFieldset('required');
390
- const requiredNestedFieldsetAddButton = within(
391
- requiredNestedFieldset
392
- ).getByRole('button', {
393
- name: 'Προσθήκη',
394
- });
395
- fireEvent.click(requiredNestedFieldsetAddButton);
396
- expect(screen.getAllByRole('dialog').length).toBe(2);
397
- const requiredNestedFieldsetInput = screen.getByRole('textbox');
398
- expect(requiredNestedFieldsetInput).toBeInTheDocument();
399
- expect(requiredNestedFieldsetInput).toHaveProperty(
400
- 'name',
401
- `nested_multiplicity_required`
402
- );
403
- fireEvent.change(requiredNestedFieldsetInput, {
404
- target: { value: 'Required Test Value 1' },
405
- });
406
- expect(requiredNestedFieldsetInput).toHaveValue('Required Test Value 1');
407
- // There are two "Αποθήκευση" buttons, one for the nested dialog and one for the main dialog
408
- const requiredSubmitButton = screen.getAllByRole('button', {
409
- name: 'Αποθήκευση',
410
- })[1];
411
- fireEvent.click(requiredSubmitButton);
412
- await waitFor(() => {
413
- // Only one dialog should remain open (the main one)
414
- expect(screen.getAllByRole('dialog').length).toBe(1);
415
- });
416
-
347
+ const requiredAddButton = getNestedAddButton('required');
348
+ await addNestedItem(requiredAddButton, 'Required Test Value 1');
417
349
  expect(screen.getByText('Required Test Value 1')).toBeInTheDocument();
418
350
  expect(submitHandler).not.toHaveBeenCalled();
419
- // Add a second required item
420
- fireEvent.click(requiredNestedFieldsetAddButton);
421
- expect(screen.getAllByRole('dialog').length).toBe(2);
422
- const secondRequiredNestedFieldsetInput = screen.getByRole('textbox');
423
- expect(secondRequiredNestedFieldsetInput).toBeInTheDocument();
424
- expect(secondRequiredNestedFieldsetInput).toHaveProperty(
425
- 'name',
426
- `nested_multiplicity_required`
427
- );
428
- fireEvent.change(secondRequiredNestedFieldsetInput, {
429
- target: { value: 'Required Test Value 2' },
430
- });
431
- expect(secondRequiredNestedFieldsetInput).toHaveValue(
432
- 'Required Test Value 2'
433
- );
434
- const secondRequiredSubmitButton = screen.getAllByRole('button', {
435
- name: 'Αποθήκευση',
436
- })[1];
437
- fireEvent.click(secondRequiredSubmitButton);
438
- await waitFor(() => {
439
- // Only one dialog should remain open (the main one)
440
- expect(screen.getAllByRole('dialog').length).toBe(1);
441
- });
442
351
 
352
+ await addNestedItem(requiredAddButton, 'Required Test Value 2');
443
353
  expect(screen.getByText('Required Test Value 1')).toBeInTheDocument();
444
354
  expect(submitHandler).not.toHaveBeenCalled();
445
355
 
446
356
  // Add nested_multiplicity_optional item
447
- const optionalNestedFieldset = getNestedFieldset('optional');
448
- const optionalNestedFieldsetAddButton = within(
449
- optionalNestedFieldset
450
- ).getByRole('button', { name: 'Προσθήκη' });
451
- fireEvent.click(optionalNestedFieldsetAddButton);
452
- expect(screen.getAllByRole('dialog').length).toBe(2);
453
- const optionalNestedFieldsetInput = screen.getByRole('textbox');
454
- expect(optionalNestedFieldsetInput).toBeInTheDocument();
455
- expect(optionalNestedFieldsetInput).toHaveProperty(
456
- 'name',
457
- `nested_multiplicity_optional`
458
- );
459
- fireEvent.change(optionalNestedFieldsetInput, {
460
- target: { value: 'Optional Test Value' },
461
- });
462
- expect(optionalNestedFieldsetInput).toHaveValue('Optional Test Value');
463
-
464
- // There are two "Αποθήκευση" buttons, one for the nested dialog and one for the main dialog
465
- const optionalSubmitButton = screen.getAllByRole('button', {
466
- name: 'Αποθήκευση',
467
- })[1];
468
- fireEvent.click(optionalSubmitButton);
469
- await waitFor(() => {
470
- // Only one dialog should remain open (the main one)
471
- expect(screen.getAllByRole('dialog').length).toBe(1);
472
- });
473
-
357
+ const optionalAddButton = getNestedAddButton('optional');
358
+ await addNestedItem(optionalAddButton, 'Optional Test Value');
474
359
  expect(screen.getByText('Optional Test Value')).toBeInTheDocument();
475
360
  expect(submitHandler).not.toHaveBeenCalled();
476
361
 
477
- const mainDialogSubmitButton = screen.getByRole('button', {
478
- name: 'Αποθήκευση',
479
- });
480
- fireEvent.click(mainDialogSubmitButton);
481
- await waitFor(() => {
482
- expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
483
- });
362
+ await saveMainDialog();
484
363
  expect(screen.getByText('Optional Test Value')).toBeInTheDocument();
485
- const continueButton = screen.getByRole('button', { name: 'Συνέχεια' });
486
- expect(continueButton).toBeInTheDocument();
487
- fireEvent.click(continueButton);
488
- await waitFor(() => {
489
- expect(submitHandler).toHaveBeenCalledTimes(1);
490
- });
364
+
365
+ await submitForm(submitHandler);
491
366
  expect(submitHandler).toHaveBeenCalledWith({
492
367
  nested_fieldset_multiplicity: expect.arrayContaining([
493
368
  {
@@ -501,96 +376,144 @@ describe('nested_fieldset_multiplicity case from dilosi', () => {
501
376
  });
502
377
  });
503
378
 
504
- it('should submit multiple fieldset items added to main form', async () => {
379
+ it('should have independent nested_multiplicity_required max limits per fieldset item', async () => {
505
380
  render(<TestForm />);
381
+
382
+ // --- First nested_fieldset_multiplicity item ---
506
383
  openMainDialog();
507
384
 
508
- // Add first item
509
- const nestedFieldset = getNestedFieldset('required');
510
- const nestedFieldsetAddButton = within(nestedFieldset).getByRole('button', {
511
- name: 'Προσθήκη',
512
- });
513
- fireEvent.click(nestedFieldsetAddButton);
514
- expect(screen.getAllByRole('dialog').length).toBe(2);
515
- const nestedFieldsetInput = screen.getByRole('textbox');
516
- expect(nestedFieldsetInput).toBeInTheDocument();
517
- expect(nestedFieldsetInput).toHaveProperty(
518
- 'name',
519
- `nested_multiplicity_required`
520
- );
521
- fireEvent.change(nestedFieldsetInput, {
522
- target: { value: 'Test Value 1' },
523
- });
524
- expect(nestedFieldsetInput).toHaveValue('Test Value 1');
525
- // There are two "Αποθήκευση" buttons, one for the nested dialog and one for the main dialog
526
- const submitButton = screen.getAllByRole('button', {
527
- name: 'Αποθήκευση',
528
- })[1];
529
- fireEvent.click(submitButton);
530
- await waitFor(() => {
531
- // Only one dialog should remain open (the main one)
532
- expect(screen.getAllByRole('dialog').length).toBe(1);
385
+ const nestedAddButton = getNestedAddButton('required');
386
+
387
+ // Fill all 10 (max) nested_multiplicity_required items
388
+ await addNestedItems(nestedAddButton, 'Item 1', 10);
389
+
390
+ // The "add" button for nested_multiplicity_required should now be disabled (max reached)
391
+ expect(nestedAddButton).toBeDisabled();
392
+
393
+ // Submit first fieldset item
394
+ await saveMainDialog();
395
+
396
+ // --- Second nested_fieldset_multiplicity item ---
397
+ openMainDialog();
398
+
399
+ const nestedAddButton2 = getNestedAddButton('required', true);
400
+
401
+ // The "add" button should NOT be disabled — it's a fresh fieldset item
402
+ expect(nestedAddButton2).not.toBeDisabled();
403
+
404
+ // Add a nested_multiplicity_required item to verify it works
405
+ await addNestedItems(nestedAddButton2, 'Item 2', 1);
406
+ expect(screen.getByText('Item 2-1')).toBeInTheDocument();
407
+ });
408
+
409
+ it('should not validate nested_multiplicity_optional when empty (min 0)', async () => {
410
+ render(<TestForm />);
411
+ openMainDialog();
412
+
413
+ // Add only a required item — leave optional empty
414
+ const requiredAddButton = getNestedAddButton('required');
415
+ await addNestedItem(requiredAddButton, 'Required Value');
416
+
417
+ // Save main dialog — should succeed without optional validation error
418
+ await saveMainDialog();
419
+
420
+ await submitForm(submitHandler);
421
+ expect(submitHandler).toHaveBeenCalledWith({
422
+ nested_fieldset_multiplicity: expect.arrayContaining([
423
+ {
424
+ nested_multiplicity_required: ['Required Value'],
425
+ nested_multiplicity_optional: [],
426
+ },
427
+ ]),
533
428
  });
429
+ });
430
+
431
+ it('should disable nested_multiplicity_optional add button when max (10) is reached', async () => {
432
+ render(<TestForm />);
433
+ openMainDialog();
434
+
435
+ // Must add at least 1 required item to allow saving later
436
+ const requiredAddButton = getNestedAddButton('required');
437
+ await addNestedItem(requiredAddButton, 'Required Value');
438
+
439
+ const optionalAddButton = getNestedAddButton('optional');
440
+
441
+ // Fill all 10 (max) optional items
442
+ await addNestedItems(optionalAddButton, 'Opt', 10);
443
+
444
+ // The "add" button for nested_multiplicity_optional should now be disabled
445
+ expect(optionalAddButton).toBeDisabled();
446
+ });
447
+
448
+ it('should have independent nested_multiplicity_optional max limits per fieldset item', async () => {
449
+ render(<TestForm />);
450
+
451
+ // --- First nested_fieldset_multiplicity item ---
452
+ openMainDialog();
453
+
454
+ const requiredAddButton = getNestedAddButton('required');
455
+ await addNestedItem(requiredAddButton, 'Req 1');
456
+
457
+ const optionalAddButton = getNestedAddButton('optional');
458
+ await addNestedItems(optionalAddButton, 'Opt 1', 10);
459
+
460
+ // Max reached for optional in first fieldset item
461
+ expect(optionalAddButton).toBeDisabled();
462
+
463
+ await saveMainDialog();
464
+
465
+ // --- Second nested_fieldset_multiplicity item ---
466
+ openMainDialog();
467
+
468
+ const requiredAddButton2 = getNestedAddButton('required', true);
469
+ await addNestedItem(requiredAddButton2, 'Req 2');
470
+
471
+ const optionalAddButton2 = getNestedAddButton('optional', true);
472
+
473
+ // The "add" button should NOT be disabled — it's a fresh fieldset item
474
+ expect(optionalAddButton2).not.toBeDisabled();
475
+
476
+ await addNestedItem(optionalAddButton2, 'Opt 2-1');
477
+ expect(screen.getByText('Opt 2-1')).toBeInTheDocument();
478
+ });
479
+
480
+ it('should disable outer add button when max (5) fieldset items are reached', async () => {
481
+ render(<TestForm />);
482
+
483
+ // Add 5 fieldset items (the max)
484
+ for (let i = 1; i <= 5; i++) {
485
+ await addFieldsetItem(`Value ${i}`);
486
+ }
487
+
488
+ // The outer "Προσθήκη" button should now be disabled
489
+ const outerAddButton = screen.getByRole('button', { name: 'Προσθήκη' });
490
+ expect(outerAddButton).toBeDisabled();
491
+ });
492
+
493
+ it('should submit multiple fieldset items added to main form', async () => {
494
+ render(<TestForm />);
495
+
496
+ // Add first item
497
+ openMainDialog();
498
+ const nestedAddButton = getNestedAddButton('required');
499
+ await addNestedItem(nestedAddButton, 'Test Value 1');
534
500
  expect(screen.getByText('Test Value 1')).toBeInTheDocument();
535
501
  expect(submitHandler).not.toHaveBeenCalled();
536
502
 
537
- // Submit first item
538
- const mainDialogSubmitButton = screen.getByRole('button', {
539
- name: 'Αποθήκευση',
540
- });
541
- fireEvent.click(mainDialogSubmitButton);
542
- await waitFor(() => {
543
- expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
544
- });
503
+ await saveMainDialog();
545
504
  expect(screen.getByText('Test Value 1')).toBeInTheDocument();
546
505
 
547
506
  // Add second item
548
507
  openMainDialog();
549
- const secondNestedFieldset = getNestedFieldset('required', true);
550
- const secondNestedFieldsetAddButton = within(
551
- secondNestedFieldset
552
- ).getByRole('button', {
553
- name: 'Προσθήκη',
554
- });
555
- fireEvent.click(secondNestedFieldsetAddButton);
556
- expect(screen.getAllByRole('dialog').length).toBe(2);
557
- const secondNestedFieldsetInput = screen.getByRole('textbox');
558
- expect(secondNestedFieldsetInput).toBeInTheDocument();
559
- expect(secondNestedFieldsetInput).toHaveProperty(
560
- 'name',
561
- `nested_multiplicity_required`
562
- );
563
- fireEvent.change(secondNestedFieldsetInput, {
564
- target: { value: 'Test Value 2' },
565
- });
566
- expect(secondNestedFieldsetInput).toHaveValue('Test Value 2');
567
- // There are two "Αποθήκευση" buttons, one for the nested dialog and one for the main dialog
568
- const secondSubmitButton = screen.getAllByRole('button', {
569
- name: 'Αποθήκευση',
570
- })[1];
571
- fireEvent.click(secondSubmitButton);
572
- await waitFor(() => {
573
- // Only one dialog should remain open (the main one)
574
- expect(screen.getAllByRole('dialog').length).toBe(1);
575
- });
508
+ const secondNestedAddButton = getNestedAddButton('required', true);
509
+ await addNestedItem(secondNestedAddButton, 'Test Value 2');
576
510
  expect(screen.getByText('Test Value 2')).toBeInTheDocument();
577
511
  expect(submitHandler).not.toHaveBeenCalled();
578
- // Submit second item
579
- const secondMainDialogSubmitButton = screen.getByRole('button', {
580
- name: 'Αποθήκευση',
581
- });
582
- fireEvent.click(secondMainDialogSubmitButton);
583
- await waitFor(() => {
584
- expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
585
- });
512
+
513
+ await saveMainDialog();
586
514
  expect(screen.getByText('Test Value 2')).toBeInTheDocument();
587
515
 
588
- const continueButton = screen.getByRole('button', { name: 'Συνέχεια' });
589
- expect(continueButton).toBeInTheDocument();
590
- fireEvent.click(continueButton);
591
- await waitFor(() => {
592
- expect(submitHandler).toHaveBeenCalledTimes(1);
593
- });
516
+ await submitForm(submitHandler);
594
517
  expect(submitHandler).toHaveBeenCalledWith({
595
518
  nested_fieldset_multiplicity: expect.arrayContaining([
596
519
  {
@@ -606,6 +529,8 @@ describe('nested_fieldset_multiplicity case from dilosi', () => {
606
529
  });
607
530
  });
608
531
 
532
+ // --- Helper functions ---
533
+
609
534
  function openMainDialog() {
610
535
  const addButton = screen.getByRole('button', { name: 'Προσθήκη' });
611
536
  fireEvent.click(addButton);
@@ -625,3 +550,69 @@ function getNestedFieldset(
625
550
  expect(nestedFieldset).toBeInTheDocument();
626
551
  return nestedFieldset!;
627
552
  }
553
+
554
+ function getNestedAddButton(
555
+ type: 'required' | 'optional',
556
+ hasSubmittedOnce = false
557
+ ) {
558
+ const fieldset = getNestedFieldset(type, hasSubmittedOnce);
559
+ return within(fieldset).getByRole('button', { name: 'Προσθήκη' });
560
+ }
561
+
562
+ function clickNestedSaveButton() {
563
+ const saveBtn = screen.getAllByRole('button', { name: 'Αποθήκευση' })[1];
564
+ fireEvent.click(saveBtn);
565
+ }
566
+
567
+ async function addNestedItem(addButton: HTMLElement, value: string) {
568
+ fireEvent.click(addButton);
569
+ expect(screen.getAllByRole('dialog').length).toBe(2);
570
+ const input = screen.getByRole('textbox');
571
+ fireEvent.change(input, { target: { value } });
572
+ clickNestedSaveButton();
573
+ await waitFor(() => {
574
+ expect(screen.getAllByRole('dialog').length).toBe(1);
575
+ });
576
+ }
577
+
578
+ async function addNestedItems(
579
+ addButton: HTMLElement,
580
+ prefix: string,
581
+ count: number
582
+ ) {
583
+ for (let i = 1; i <= count; i++) {
584
+ await addNestedItem(addButton, `${prefix}-${i}`);
585
+ }
586
+ }
587
+
588
+ async function saveMainDialog() {
589
+ const saveBtn = screen.getByRole('button', { name: 'Αποθήκευση' });
590
+ fireEvent.click(saveBtn);
591
+ await waitFor(() => {
592
+ expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
593
+ });
594
+ }
595
+
596
+ async function addFieldsetItem(requiredValue: string) {
597
+ openMainDialog();
598
+ // After the first submission, there are duplicate fieldset labels in the DOM.
599
+ // Always pick the last matching fieldset (the one inside the open dialog).
600
+ const requiredFieldset = screen
601
+ .getAllByText(/"required" μέχρι 10/)
602
+ .at(-1)!
603
+ .closest('fieldset')!;
604
+ const nestedAddButton = within(requiredFieldset).getByRole('button', {
605
+ name: 'Προσθήκη',
606
+ });
607
+ await addNestedItem(nestedAddButton, requiredValue);
608
+ await saveMainDialog();
609
+ }
610
+
611
+ async function submitForm(submitHandler: ReturnType<typeof vi.fn>) {
612
+ const continueButton = screen.getByRole('button', { name: 'Συνέχεια' });
613
+ expect(continueButton).toBeInTheDocument();
614
+ fireEvent.click(continueButton);
615
+ await waitFor(() => {
616
+ expect(submitHandler).toHaveBeenCalledTimes(1);
617
+ });
618
+ }
package/src/lazy.ts CHANGED
@@ -9,10 +9,10 @@ export default {
9
9
  'FieldBase': lazy(() => import('@digigov/form/Field/FieldBase').then((module) => ({ default: module['FieldBase'] }))),
10
10
  'FieldBaseContainer': lazy(() => import('@digigov/form/Field/FieldBaseContainer').then((module) => ({ default: module['FieldBaseContainer'] }))),
11
11
  'FieldConditional': lazy(() => import('@digigov/form/Field/FieldConditional').then((module) => ({ default: module['FieldConditional'] }))),
12
- 'FieldObject': lazy(() => import('@digigov/form/FieldObject').then((module) => ({ default: module['FieldObject'] }))),
13
12
  'BaseFieldArray': lazy(() => import('@digigov/form/FieldArray/BaseFieldArray').then((module) => ({ default: module['BaseFieldArray'] }))),
14
13
  'DeleteConfirmationModal': lazy(() => import('@digigov/form/FieldArray/DeleteConfirmationModal').then((module) => ({ default: module['DeleteConfirmationModal'] }))),
15
14
  'FieldArray': lazy(() => import('@digigov/form/FieldArray').then((module) => ({ default: module['FieldArray'] }))),
15
+ 'FieldObject': lazy(() => import('@digigov/form/FieldObject').then((module) => ({ default: module['FieldObject'] }))),
16
16
  'FieldsetWithContext': lazy(() => import('@digigov/form/Fieldset/FieldsetWithContext').then((module) => ({ default: module['FieldsetWithContext'] }))),
17
17
  'FieldsetLabel': lazy(() => import('@digigov/form/Fieldset').then((module) => ({ default: module['FieldsetLabel'] }))),
18
18
  'FieldsetCaption': lazy(() => import('@digigov/form/Fieldset').then((module) => ({ default: module['FieldsetCaption'] }))),
package/src/registry.ts CHANGED
@@ -10,10 +10,10 @@ import * as _digigov_form_Field_FieldBaseContainer from "@digigov/form/Field/Fie
10
10
  import * as _digigov_form_Field_FieldConditional from "@digigov/form/Field/FieldConditional";
11
11
  import * as _digigov_form_Field from "@digigov/form/Field";
12
12
  import * as _digigov_form_Field_types from "@digigov/form/Field/types";
13
- import * as _digigov_form_FieldObject from "@digigov/form/FieldObject";
14
13
  import * as _digigov_form_FieldArray_BaseFieldArray from "@digigov/form/FieldArray/BaseFieldArray";
15
14
  import * as _digigov_form_FieldArray_DeleteConfirmationModal from "@digigov/form/FieldArray/DeleteConfirmationModal";
16
15
  import * as _digigov_form_FieldArray from "@digigov/form/FieldArray";
16
+ import * as _digigov_form_FieldObject from "@digigov/form/FieldObject";
17
17
  import * as _digigov_form_Fieldset_FieldsetWithContext from "@digigov/form/Fieldset/FieldsetWithContext";
18
18
  import * as _digigov_form_Fieldset from "@digigov/form/Fieldset";
19
19
  import * as _digigov_form_Fieldset_types from "@digigov/form/Fieldset/types";
@@ -49,9 +49,9 @@ import * as _digigov_form_Questions_Step_getAddMoreFields from "@digigov/form/Qu
49
49
  import * as _digigov_form_Questions_Step from "@digigov/form/Questions/Step";
50
50
  import * as _digigov_form_Questions_Step_types from "@digigov/form/Questions/Step/types";
51
51
  import * as _digigov_form_inputs_AutoCompleteInput from "@digigov/form/inputs/AutoCompleteInput";
52
+ import * as _digigov_form_inputs_DateTimeInput from "@digigov/form/inputs/DateTimeInput";
52
53
  import * as _digigov_form_inputs_Checkboxes from "@digigov/form/inputs/Checkboxes";
53
54
  import * as _digigov_form_inputs_DateInput from "@digigov/form/inputs/DateInput";
54
- import * as _digigov_form_inputs_DateTimeInput from "@digigov/form/inputs/DateTimeInput";
55
55
  import * as _digigov_form_inputs_FileInput from "@digigov/form/inputs/FileInput";
56
56
  import * as _digigov_form_inputs_ImageInput from "@digigov/form/inputs/ImageInput";
57
57
  import * as _digigov_form_inputs_Input from "@digigov/form/inputs/Input";
@@ -109,10 +109,10 @@ export default {
109
109
  '@digigov/form/Field/FieldConditional': lazyImport(_digigov_form_Field_FieldConditional),
110
110
  '@digigov/form/Field': lazyImport(_digigov_form_Field),
111
111
  '@digigov/form/Field/types': lazyImport(_digigov_form_Field_types),
112
- '@digigov/form/FieldObject': lazyImport(_digigov_form_FieldObject),
113
112
  '@digigov/form/FieldArray/BaseFieldArray': lazyImport(_digigov_form_FieldArray_BaseFieldArray),
114
113
  '@digigov/form/FieldArray/DeleteConfirmationModal': lazyImport(_digigov_form_FieldArray_DeleteConfirmationModal),
115
114
  '@digigov/form/FieldArray': lazyImport(_digigov_form_FieldArray),
115
+ '@digigov/form/FieldObject': lazyImport(_digigov_form_FieldObject),
116
116
  '@digigov/form/Fieldset/FieldsetWithContext': lazyImport(_digigov_form_Fieldset_FieldsetWithContext),
117
117
  '@digigov/form/Fieldset': lazyImport(_digigov_form_Fieldset),
118
118
  '@digigov/form/Fieldset/types': lazyImport(_digigov_form_Fieldset_types),
@@ -148,9 +148,9 @@ export default {
148
148
  '@digigov/form/Questions/Step': lazyImport(_digigov_form_Questions_Step),
149
149
  '@digigov/form/Questions/Step/types': lazyImport(_digigov_form_Questions_Step_types),
150
150
  '@digigov/form/inputs/AutoCompleteInput': lazyImport(_digigov_form_inputs_AutoCompleteInput),
151
+ '@digigov/form/inputs/DateTimeInput': lazyImport(_digigov_form_inputs_DateTimeInput),
151
152
  '@digigov/form/inputs/Checkboxes': lazyImport(_digigov_form_inputs_Checkboxes),
152
153
  '@digigov/form/inputs/DateInput': lazyImport(_digigov_form_inputs_DateInput),
153
- '@digigov/form/inputs/DateTimeInput': lazyImport(_digigov_form_inputs_DateTimeInput),
154
154
  '@digigov/form/inputs/FileInput': lazyImport(_digigov_form_inputs_FileInput),
155
155
  '@digigov/form/inputs/ImageInput': lazyImport(_digigov_form_inputs_ImageInput),
156
156
  '@digigov/form/inputs/Input': lazyImport(_digigov_form_inputs_Input),