@digigov/form 2.0.9 → 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 +19 -2
- package/FieldArray/BaseFieldArray.d.ts +3 -2
- package/FieldArray/BaseFieldArray.js +26 -4
- package/FieldArray/DeleteConfirmationModal.d.ts +8 -0
- package/FieldArray/DeleteConfirmationModal.js +29 -0
- package/FieldArray/FormDialog/ArrayDisplay/ArrayItemDisplay.js +3 -1
- package/FieldArray/FormDialog/ArrayDisplay/index.d.ts +2 -1
- package/FieldArray/FormDialog/ArrayDisplay/index.js +39 -6
- package/FieldArray/FormDialog/index.d.ts +1 -0
- package/FieldArray/FormDialog/index.js +4 -3
- package/FieldArray/index.js +2 -0
- package/index.js +1 -1
- package/lazy.d.ts +4 -34
- package/lazy.js +3 -0
- package/package.json +4 -4
- package/registry.d.ts +3 -2
- package/registry.js +6 -4
- package/src/FieldArray/BaseFieldArray.tsx +34 -3
- package/src/FieldArray/DeleteConfirmationModal.tsx +57 -0
- package/src/FieldArray/FieldArray.stories.js +1 -0
- package/src/FieldArray/FormDialog/ArrayDisplay/ArrayDisplay.stories.js +1 -0
- package/src/FieldArray/FormDialog/ArrayDisplay/ArrayItemDisplay.tsx +3 -1
- package/src/FieldArray/FormDialog/ArrayDisplay/__stories__/WithDeleteConfirmation.tsx +94 -0
- package/src/FieldArray/FormDialog/ArrayDisplay/index.test.tsx +30 -26
- package/src/FieldArray/FormDialog/ArrayDisplay/index.tsx +70 -36
- package/src/FieldArray/FormDialog/index.tsx +5 -1
- package/src/FieldArray/__stories__/WithDeleteConfirmation.tsx +190 -0
- package/src/FieldArray/__tests__/nested-fieldset-multiplicity.spec.tsx +210 -219
- package/src/FieldArray/index.spec.tsx +42 -0
- package/src/FieldArray/index.test.tsx +27 -23
- package/src/FieldArray/index.tsx +2 -1
- package/src/lazy.ts +1 -0
- package/src/registry.ts +6 -4
|
@@ -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
|
|
276
|
-
|
|
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
|
|
310
|
-
|
|
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
|
-
|
|
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
|
-
|
|
332
|
-
const
|
|
333
|
-
|
|
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
|
-
|
|
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
|
-
|
|
369
|
-
|
|
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
|
|
390
|
-
|
|
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
|
|
448
|
-
|
|
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
|
-
|
|
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
|
-
|
|
486
|
-
|
|
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
|
|
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
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
expect(
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
);
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
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
|
-
|
|
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
|
|
550
|
-
|
|
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
|
-
|
|
579
|
-
|
|
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
|
-
|
|
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
|
+
}
|
|
@@ -351,5 +351,47 @@ describe('FieldArray', () => {
|
|
|
351
351
|
|
|
352
352
|
expect(screen.getByText('Προσθήκη συνυπογράφοντα')).toHaveFocus();
|
|
353
353
|
});
|
|
354
|
+
|
|
355
|
+
it('should show delete confirmation modal and delete item on confirm', async () => {
|
|
356
|
+
render(
|
|
357
|
+
<TestForm
|
|
358
|
+
fieldObject={{
|
|
359
|
+
...fieldSpec,
|
|
360
|
+
extra: {
|
|
361
|
+
...fieldSpec.extra,
|
|
362
|
+
deleteConfirmation: true,
|
|
363
|
+
},
|
|
364
|
+
}}
|
|
365
|
+
/>
|
|
366
|
+
);
|
|
367
|
+
|
|
368
|
+
const addButton = await screen.findByText('Προσθήκη συνυπογράφοντα');
|
|
369
|
+
expect(addButton).toBeInTheDocument();
|
|
370
|
+
fireEvent.click(addButton);
|
|
371
|
+
|
|
372
|
+
const nameInput = screen.getByLabelText('Όνομα');
|
|
373
|
+
fireEvent.change(nameInput, {
|
|
374
|
+
target: { value: 'John' },
|
|
375
|
+
});
|
|
376
|
+
fireEvent.click(screen.getByText('Αποθήκευση'));
|
|
377
|
+
|
|
378
|
+
await waitFor(() =>
|
|
379
|
+
expect(screen.queryByRole('dialog')).not.toBeInTheDocument()
|
|
380
|
+
);
|
|
381
|
+
await waitFor(() => expect(screen.getByText('John')).toBeInTheDocument());
|
|
382
|
+
|
|
383
|
+
fireEvent.click(screen.getByText('Διαγραφή'));
|
|
384
|
+
|
|
385
|
+
await waitFor(() =>
|
|
386
|
+
expect(screen.getByRole('dialog')).toBeInTheDocument()
|
|
387
|
+
);
|
|
388
|
+
expect(screen.getByText('Επιβεβαίωση διαγραφής')).toBeInTheDocument();
|
|
389
|
+
fireEvent.click(screen.getAllByText('Διαγραφή')[1]);
|
|
390
|
+
await waitFor(() =>
|
|
391
|
+
expect(screen.queryByRole('dialog')).not.toBeInTheDocument()
|
|
392
|
+
);
|
|
393
|
+
|
|
394
|
+
expect(screen.queryByText('John')).not.toBeInTheDocument();
|
|
395
|
+
});
|
|
354
396
|
});
|
|
355
397
|
});
|
|
@@ -1,40 +1,44 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { test, expect } from '@playwright/experimental-ct-react';
|
|
3
|
-
import TestVariant from '@digigov/ui/utils/TestVariant'
|
|
3
|
+
import TestVariant from '@digigov/ui/utils/TestVariant';
|
|
4
4
|
import { CardsWithError } from '@digigov/form/FieldArray/__stories__/CardsWithError';
|
|
5
5
|
import { Default } from '@digigov/form/FieldArray/__stories__/Default';
|
|
6
6
|
import { TableWithError } from '@digigov/form/FieldArray/__stories__/TableWithError';
|
|
7
7
|
import { WithExactLength } from '@digigov/form/FieldArray/__stories__/WithExactLength';
|
|
8
8
|
import { WithModal } from '@digigov/form/FieldArray/__stories__/WithModal';
|
|
9
|
+
import { WithDeleteConfirmation } from '@digigov/form/FieldArray/__stories__/WithDeleteConfirmation';
|
|
9
10
|
|
|
10
11
|
test('renders the All FieldArray variants', async ({ mount, page }) => {
|
|
11
12
|
await mount(
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
13
|
+
<div>
|
|
14
|
+
<TestVariant title="CardsWithError">
|
|
15
|
+
<CardsWithError />
|
|
16
|
+
</TestVariant>
|
|
17
|
+
<TestVariant title="Default">
|
|
18
|
+
<Default />
|
|
19
|
+
</TestVariant>
|
|
20
|
+
<TestVariant title="TableWithError">
|
|
21
|
+
<TableWithError />
|
|
22
|
+
</TestVariant>
|
|
23
|
+
<TestVariant title="WithExactLength">
|
|
24
|
+
<WithExactLength />
|
|
25
|
+
</TestVariant>
|
|
26
|
+
<TestVariant title="WithModal">
|
|
27
|
+
<WithModal />
|
|
28
|
+
</TestVariant>
|
|
29
|
+
<TestVariant title="WithDeleteConfirmation">
|
|
30
|
+
<WithDeleteConfirmation />
|
|
31
|
+
</TestVariant>
|
|
32
|
+
</div>
|
|
33
|
+
);
|
|
31
34
|
await page.evaluate(() => document.fonts.ready);
|
|
32
35
|
|
|
33
36
|
// Move the mouse to the top-left corner to avoid random hover issues
|
|
34
37
|
await page.mouse.move(0, 0);
|
|
35
38
|
|
|
36
|
-
|
|
37
|
-
|
|
39
|
+
const screenshot = await page.screenshot({
|
|
40
|
+
fullPage: true,
|
|
41
|
+
animations: 'disabled',
|
|
42
|
+
});
|
|
38
43
|
expect(screenshot).toMatchSnapshot();
|
|
39
44
|
});
|
|
40
|
-
|
package/src/FieldArray/index.tsx
CHANGED
|
@@ -40,7 +40,6 @@ export const FieldArray: React.FC<FieldArrayProps> = ({
|
|
|
40
40
|
}) => {
|
|
41
41
|
/** The ref of the add-button */
|
|
42
42
|
const buttonRef = useRef<HTMLButtonElement | null>(null);
|
|
43
|
-
|
|
44
43
|
// Register the button to be focusable
|
|
45
44
|
useEffect(() => {
|
|
46
45
|
let unregister: (name: string) => void = () => {};
|
|
@@ -90,6 +89,7 @@ export const FieldArray: React.FC<FieldArrayProps> = ({
|
|
|
90
89
|
reset={reset}
|
|
91
90
|
resetField={resetField}
|
|
92
91
|
sortable={customField.extra.sortable}
|
|
92
|
+
deleteConfirmation={customField.extra?.deleteConfirmation}
|
|
93
93
|
{...customField}
|
|
94
94
|
/>
|
|
95
95
|
) : (
|
|
@@ -102,6 +102,7 @@ export const FieldArray: React.FC<FieldArrayProps> = ({
|
|
|
102
102
|
error={error}
|
|
103
103
|
getValues={getValues}
|
|
104
104
|
Field={Field}
|
|
105
|
+
deleteConfirmation={customField.extra?.deleteConfirmation}
|
|
105
106
|
{...customField}
|
|
106
107
|
/>
|
|
107
108
|
)}
|
package/src/lazy.ts
CHANGED
|
@@ -10,6 +10,7 @@ export default {
|
|
|
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
12
|
'BaseFieldArray': lazy(() => import('@digigov/form/FieldArray/BaseFieldArray').then((module) => ({ default: module['BaseFieldArray'] }))),
|
|
13
|
+
'DeleteConfirmationModal': lazy(() => import('@digigov/form/FieldArray/DeleteConfirmationModal').then((module) => ({ default: module['DeleteConfirmationModal'] }))),
|
|
13
14
|
'FieldArray': lazy(() => import('@digigov/form/FieldArray').then((module) => ({ default: module['FieldArray'] }))),
|
|
14
15
|
'FieldObject': lazy(() => import('@digigov/form/FieldObject').then((module) => ({ default: module['FieldObject'] }))),
|
|
15
16
|
'FieldsetWithContext': lazy(() => import('@digigov/form/Fieldset/FieldsetWithContext').then((module) => ({ default: module['FieldsetWithContext'] }))),
|