cozy-ui-plus 3.1.2 → 4.1.0
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 +18 -0
- package/dist/AppSections/Sections.js +5 -7
- package/dist/AppSections/components/AppsSection.js +2 -2
- package/dist/AppTile/index.js +3 -3
- package/dist/Contacts/AddModal/ContactAddressDialog/index.js +4 -4
- package/dist/Contacts/AddModal/ContactForm/FieldInput.js +6 -10
- package/dist/Contacts/AddModal/ContactForm/FieldInputAccordion.js +4 -4
- package/dist/Contacts/AddModal/ContactForm/FieldInputArray.js +7 -9
- package/dist/Contacts/AddModal/ContactForm/FieldInputLayout.js +6 -8
- package/dist/Contacts/AddModal/ContactForm/FieldInputWrapper.js +1 -1
- package/dist/Contacts/AddModal/ContactForm/TextFieldCustomLabelSelect.js +3 -3
- package/dist/Contacts/AddModal/ContactForm/TextFieldSelect.js +4 -4
- package/dist/Contacts/AddModal/ContactForm/helpers.js +90 -33
- package/dist/Contacts/AddModal/ContactForm/index.js +10 -5
- package/dist/Contacts/AddModal/ContactForm/locales/index.js +4 -4
- package/dist/Contacts/AddModal/CustomLabelDialog/index.js +4 -4
- package/dist/Contacts/AddModal/index.js +4 -4
- package/dist/Contacts/GroupsSelect/GroupCreation.js +3 -3
- package/dist/Contacts/GroupsSelect/GroupsSelectProvider.js +3 -3
- package/dist/Contacts/GroupsSelect/SelectBox/Control.js +4 -4
- package/dist/Contacts/GroupsSelect/useGroupSelect.js +4 -4
- package/dist/Contacts/Header/GroupsSelection.js +4 -4
- package/dist/Contacts/Header/ImportDropdown.js +4 -4
- package/dist/Contacts/Header/index.js +4 -4
- package/dist/ContactsList/Contacts/ContactIdentity.js +4 -4
- package/dist/ContactsList/ContactsList.js +3 -3
- package/dist/ContactsList/locales/withContactsListLocales.js +2 -4
- package/dist/ContactsListModal/ContactsListContent.js +3 -3
- package/dist/ContactsListModal/ContactsListModal.js +3 -3
- package/dist/ContactsListModal/withContactsListLocales.js +2 -4
- package/dist/Dialogs/AuthentificationDialog/index.js +2 -2
- package/dist/Dialogs/AuthentificationDialog/withSpecificDialogsLocales.js +2 -4
- package/dist/FilePicker/FilePickerBodyItem.js +3 -3
- package/dist/FilePicker/FilePickerFooter.js +3 -3
- package/dist/FilePicker/docs/DemoProvider.js +3 -3
- package/dist/ListItem/ExpandedAttributes/ExpandedAttribute.js +3 -3
- package/dist/ListItem/ExpandedAttributes/index.js +3 -3
- package/dist/ListItem/ListItemBase/Renaming/RenameDialog.js +3 -3
- package/dist/ListItem/ListItemFile/ExpirationAnnotation.js +3 -3
- package/dist/ListItem/ListItemFile/SecondaryText.js +3 -3
- package/dist/ListItem/hoc/withListItemLocales.js +2 -4
- package/dist/Paywall/Paywall.js +3 -3
- package/dist/Paywall/locales/withPaywallLocales.js +2 -4
- package/dist/Qualification/QualificationGrid/index.js +3 -3
- package/dist/Qualification/QualificationGrid/locales/withLocales.js +2 -4
- package/dist/Qualification/QualificationModal/index.js +4 -4
- package/dist/Storage/StorageButton.js +4 -4
- package/dist/Storage/StorageProgress.js +4 -4
- package/dist/UploadQueue/Item.js +3 -3
- package/dist/UploadQueue/Pending.js +3 -3
- package/dist/UploadQueue/RemainingTime.js +3 -3
- package/dist/UploadQueue/index.js +4 -6
- package/dist/src/Contacts/AddModal/ContactForm/FieldInput.d.ts +2 -6
- package/dist/src/Contacts/AddModal/ContactForm/FieldInputArray.d.ts +2 -6
- package/dist/src/Contacts/AddModal/ContactForm/helpers.d.ts +4 -2
- package/dist/test/I18n.d.ts +1 -1
- package/package.json +7 -5
- package/src/AppSections/Readme.md +1 -1
- package/src/AppSections/Sections.jsx +1 -2
- package/src/AppSections/components/AppsSection.jsx +1 -1
- package/src/AppSections/helpers.spec.js +1 -1
- package/src/AppTile/AppTile.spec.jsx +1 -2
- package/src/AppTile/Readme.md +1 -1
- package/src/AppTile/index.jsx +1 -1
- package/src/Contacts/AddModal/ContactAddressDialog/index.jsx +1 -1
- package/src/Contacts/AddModal/ContactForm/FieldInput.jsx +4 -9
- package/src/Contacts/AddModal/ContactForm/FieldInputAccordion.jsx +1 -1
- package/src/Contacts/AddModal/ContactForm/FieldInputArray.jsx +8 -5
- package/src/Contacts/AddModal/ContactForm/FieldInputLayout.jsx +4 -4
- package/src/Contacts/AddModal/ContactForm/FieldInputWrapper.jsx +1 -1
- package/src/Contacts/AddModal/ContactForm/TextFieldCustomLabelSelect.jsx +1 -2
- package/src/Contacts/AddModal/ContactForm/TextFieldSelect.jsx +1 -1
- package/src/Contacts/AddModal/ContactForm/helpers.js +65 -17
- package/src/Contacts/AddModal/ContactForm/helpers.spec.js +344 -1
- package/src/Contacts/AddModal/ContactForm/index.jsx +11 -6
- package/src/Contacts/AddModal/ContactForm/locales/en.json +1 -1
- package/src/Contacts/AddModal/ContactForm/locales/fr.json +1 -1
- package/src/Contacts/AddModal/ContactForm/locales/ru.json +1 -1
- package/src/Contacts/AddModal/ContactForm/locales/vi.json +1 -1
- package/src/Contacts/AddModal/CustomLabelDialog/index.jsx +1 -1
- package/src/Contacts/AddModal/index.jsx +1 -1
- package/src/Contacts/GroupsSelect/GroupCreation.jsx +1 -1
- package/src/Contacts/GroupsSelect/GroupsSelectProvider.jsx +1 -2
- package/src/Contacts/GroupsSelect/SelectBox/Control.jsx +1 -1
- package/src/Contacts/GroupsSelect/useGroupSelect.jsx +1 -1
- package/src/Contacts/Header/GroupsSelection.jsx +1 -1
- package/src/Contacts/Header/ImportDropdown.jsx +1 -1
- package/src/Contacts/Header/index.jsx +1 -1
- package/src/ContactsList/Contacts/ContactIdentity.jsx +1 -1
- package/src/ContactsList/ContactsList.jsx +1 -1
- package/src/ContactsList/locales/withContactsListLocales.jsx +1 -1
- package/src/ContactsListModal/ContactsListContent.jsx +1 -1
- package/src/ContactsListModal/ContactsListModal.jsx +1 -1
- package/src/ContactsListModal/Readme.md +1 -1
- package/src/ContactsListModal/withContactsListLocales.jsx +1 -1
- package/src/Dialogs/AuthentificationDialog/index.jsx +1 -1
- package/src/Dialogs/AuthentificationDialog/withSpecificDialogsLocales.jsx +1 -1
- package/src/FilePicker/FilePickerBodyItem.jsx +1 -1
- package/src/FilePicker/FilePickerFooter.jsx +1 -1
- package/src/FilePicker/docs/DemoProvider.jsx +1 -1
- package/src/ListItem/ExpandedAttributes/ExpandedAttribute.jsx +1 -1
- package/src/ListItem/ExpandedAttributes/index.jsx +1 -1
- package/src/ListItem/ListItemBase/Renaming/RenameDialog.jsx +1 -1
- package/src/ListItem/ListItemFile/ExpirationAnnotation.jsx +1 -1
- package/src/ListItem/ListItemFile/SecondaryText.jsx +1 -1
- package/src/ListItem/hoc/withListItemLocales.jsx +1 -1
- package/src/Paywall/Paywall.jsx +1 -1
- package/src/Paywall/locales/withPaywallLocales.jsx +1 -1
- package/src/Qualification/QualificationGrid/index.jsx +1 -1
- package/src/Qualification/QualificationGrid/locales/withLocales.jsx +1 -1
- package/src/Qualification/QualificationModal/index.jsx +1 -1
- package/src/Storage/StorageButton.jsx +1 -1
- package/src/Storage/StorageProgress.jsx +1 -1
- package/src/UploadQueue/Item.jsx +1 -1
- package/src/UploadQueue/Pending.jsx +1 -1
- package/src/UploadQueue/RemainingTime.jsx +1 -1
- package/src/UploadQueue/index.jsx +1 -2
- package/src/UploadQueue/index.spec.jsx +48 -34
- package/test/I18n.js +1 -1
|
@@ -9,26 +9,63 @@ import { CONTACTS_DOCTYPE } from 'cozy-client/dist/models/contact'
|
|
|
9
9
|
|
|
10
10
|
import contactToFormValues from './contactToFormValues'
|
|
11
11
|
|
|
12
|
-
export const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
12
|
+
export const getFirstValueIfArray = values => {
|
|
13
|
+
return Object.keys(values).reduce((acc, key) => {
|
|
14
|
+
const val = values[key]
|
|
15
|
+
acc[key] = Array.isArray(val) ? val[0] : val
|
|
16
|
+
return acc
|
|
17
|
+
}, {})
|
|
18
|
+
}
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
|
-
* Returns
|
|
21
|
+
* Returns true if all fields have no values
|
|
21
22
|
* @param {object} values - Fields values
|
|
22
|
-
* @param {
|
|
23
|
+
* @param {import('../types').Field[]} fields - Fields configuration
|
|
24
|
+
* @returns {boolean} True if all fields have no values
|
|
25
|
+
*/
|
|
26
|
+
export const hasNoValues = (values, fields) => {
|
|
27
|
+
const _values = getFirstValueIfArray(values)
|
|
28
|
+
|
|
29
|
+
return fields.every(field => !get(_values, field.name))
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Returns true if the input is required and the form is invalid
|
|
34
|
+
* @param {boolean} required - Whether the input is required
|
|
35
|
+
* @param {object} formProps - The form props
|
|
36
|
+
* @returns {boolean} True if the input is required and the form is invalid
|
|
37
|
+
*/
|
|
38
|
+
export const makeIsRequiredError = (required, formProps) => {
|
|
39
|
+
const { valid, submitFailed } = formProps
|
|
40
|
+
|
|
41
|
+
return required && !valid && submitFailed
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Returns errors for required fields that are empty
|
|
46
|
+
* @param {object} values - Fields values
|
|
47
|
+
* @param {import('../types').Field[]} fields - Fields configuration
|
|
48
|
+
* @param {Function} t - Translation function
|
|
23
49
|
* @returns {object} Errors
|
|
24
50
|
*/
|
|
25
|
-
export const validateFields = (values, t) => {
|
|
51
|
+
export const validateFields = (values, fields, t) => {
|
|
26
52
|
const errors = {}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
53
|
+
const fieldsRequired = fields.filter(field => field.required)
|
|
54
|
+
const _values = getFirstValueIfArray(values)
|
|
55
|
+
|
|
56
|
+
fieldsRequired.forEach(field => {
|
|
57
|
+
if (!get(_values, field.name)) {
|
|
58
|
+
errors[field.name] =
|
|
59
|
+
field.layout === 'array'
|
|
60
|
+
? [
|
|
61
|
+
{
|
|
62
|
+
[field.name]: t('Contacts.AddModal.ContactForm.fields.required')
|
|
63
|
+
}
|
|
64
|
+
]
|
|
65
|
+
: t('Contacts.AddModal.ContactForm.fields.required')
|
|
66
|
+
}
|
|
67
|
+
})
|
|
68
|
+
|
|
32
69
|
return errors
|
|
33
70
|
}
|
|
34
71
|
|
|
@@ -384,9 +421,20 @@ export const makeFields = (customFields, defaultFields) => {
|
|
|
384
421
|
|
|
385
422
|
const fields = [...defaultFields]
|
|
386
423
|
|
|
387
|
-
customFields.
|
|
388
|
-
|
|
389
|
-
|
|
424
|
+
customFields.forEach(customField => {
|
|
425
|
+
const defaultField = fields.find(field => field.name === customField.name)
|
|
426
|
+
const _field = defaultField || customField
|
|
427
|
+
|
|
428
|
+
if (defaultField) {
|
|
429
|
+
Object.assign(_field, customField)
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
if (_field.position) {
|
|
433
|
+
if (defaultField) {
|
|
434
|
+
const fieldIndex = fields.indexOf(defaultField)
|
|
435
|
+
fields.splice(fieldIndex, 1)
|
|
436
|
+
}
|
|
437
|
+
fields.splice(_field.position, 0, _field)
|
|
390
438
|
}
|
|
391
439
|
})
|
|
392
440
|
|
|
@@ -7,7 +7,10 @@ import {
|
|
|
7
7
|
makeImppValues,
|
|
8
8
|
makeCustomLabel,
|
|
9
9
|
makeFields,
|
|
10
|
-
makeInitialCustomValue
|
|
10
|
+
makeInitialCustomValue,
|
|
11
|
+
hasNoValues,
|
|
12
|
+
getFirstValueIfArray,
|
|
13
|
+
validateFields
|
|
11
14
|
} from './helpers'
|
|
12
15
|
import { locales } from './locales'
|
|
13
16
|
|
|
@@ -388,4 +391,344 @@ describe('makeFields', () => {
|
|
|
388
391
|
|
|
389
392
|
expect(res).toStrictEqual(defaultFields)
|
|
390
393
|
})
|
|
394
|
+
|
|
395
|
+
it('should replace default fields values by custom fields', () => {
|
|
396
|
+
const defaultFields = [
|
|
397
|
+
{ name: 'firstname', type: 'text', layout: 'accordion' },
|
|
398
|
+
{ name: 'lastname' }
|
|
399
|
+
]
|
|
400
|
+
const customFields = [
|
|
401
|
+
{ name: 'firstname', isSecondary: true, layout: 'array' }
|
|
402
|
+
]
|
|
403
|
+
|
|
404
|
+
const res = makeFields(customFields, defaultFields)
|
|
405
|
+
|
|
406
|
+
expect(res).toStrictEqual([
|
|
407
|
+
{ name: 'firstname', type: 'text', isSecondary: true, layout: 'array' },
|
|
408
|
+
{ name: 'lastname' }
|
|
409
|
+
])
|
|
410
|
+
})
|
|
411
|
+
|
|
412
|
+
it('should replace default fields values and add custom fields at custom position', () => {
|
|
413
|
+
const defaultFields = [
|
|
414
|
+
{ name: 'firstname', type: 'text', layout: 'accordion' },
|
|
415
|
+
{ name: 'lastname' }
|
|
416
|
+
]
|
|
417
|
+
const customFields = [
|
|
418
|
+
{ name: 'firstname', isSecondary: true, layout: 'array', position: 1 }
|
|
419
|
+
]
|
|
420
|
+
|
|
421
|
+
const res = makeFields(customFields, defaultFields)
|
|
422
|
+
|
|
423
|
+
expect(res).toStrictEqual([
|
|
424
|
+
{ name: 'lastname' },
|
|
425
|
+
{
|
|
426
|
+
name: 'firstname',
|
|
427
|
+
type: 'text',
|
|
428
|
+
isSecondary: true,
|
|
429
|
+
layout: 'array',
|
|
430
|
+
position: 1
|
|
431
|
+
}
|
|
432
|
+
])
|
|
433
|
+
})
|
|
434
|
+
})
|
|
435
|
+
|
|
436
|
+
describe('hasNoValues', () => {
|
|
437
|
+
it('should return true when all fields have no values', () => {
|
|
438
|
+
const values = {}
|
|
439
|
+
const fields = [{ name: 'firstname' }, { name: 'lastname' }]
|
|
440
|
+
|
|
441
|
+
const res = hasNoValues(values, fields)
|
|
442
|
+
|
|
443
|
+
expect(res).toBe(true)
|
|
444
|
+
})
|
|
445
|
+
|
|
446
|
+
it('should return true when all field values are undefined', () => {
|
|
447
|
+
const values = { firstname: undefined, lastname: undefined }
|
|
448
|
+
const fields = [{ name: 'firstname' }, { name: 'lastname' }]
|
|
449
|
+
|
|
450
|
+
const res = hasNoValues(values, fields)
|
|
451
|
+
|
|
452
|
+
expect(res).toBe(true)
|
|
453
|
+
})
|
|
454
|
+
|
|
455
|
+
it('should return true when all field values are null', () => {
|
|
456
|
+
const values = { firstname: null, lastname: null }
|
|
457
|
+
const fields = [{ name: 'firstname' }, { name: 'lastname' }]
|
|
458
|
+
|
|
459
|
+
const res = hasNoValues(values, fields)
|
|
460
|
+
|
|
461
|
+
expect(res).toBe(true)
|
|
462
|
+
})
|
|
463
|
+
|
|
464
|
+
it('should return true when all field values are empty strings', () => {
|
|
465
|
+
const values = { firstname: '', lastname: '' }
|
|
466
|
+
const fields = [{ name: 'firstname' }, { name: 'lastname' }]
|
|
467
|
+
|
|
468
|
+
const res = hasNoValues(values, fields)
|
|
469
|
+
|
|
470
|
+
expect(res).toBe(true)
|
|
471
|
+
})
|
|
472
|
+
|
|
473
|
+
it('should return true when all field values are empty arrays', () => {
|
|
474
|
+
const values = { email: [], phone: [] }
|
|
475
|
+
const fields = [{ name: 'email' }, { name: 'phone' }]
|
|
476
|
+
|
|
477
|
+
const res = hasNoValues(values, fields)
|
|
478
|
+
|
|
479
|
+
expect(res).toBe(true)
|
|
480
|
+
})
|
|
481
|
+
|
|
482
|
+
it('should return false when at least one field has a value', () => {
|
|
483
|
+
const values = { firstname: 'John', lastname: undefined }
|
|
484
|
+
const fields = [{ name: 'firstname' }, { name: 'lastname' }]
|
|
485
|
+
|
|
486
|
+
const res = hasNoValues(values, fields)
|
|
487
|
+
|
|
488
|
+
expect(res).toBe(false)
|
|
489
|
+
})
|
|
490
|
+
|
|
491
|
+
it('should return false when at least one field has a non-empty string', () => {
|
|
492
|
+
const values = { firstname: '', lastname: 'Doe' }
|
|
493
|
+
const fields = [{ name: 'firstname' }, { name: 'lastname' }]
|
|
494
|
+
|
|
495
|
+
const res = hasNoValues(values, fields)
|
|
496
|
+
|
|
497
|
+
expect(res).toBe(false)
|
|
498
|
+
})
|
|
499
|
+
|
|
500
|
+
it('should return false when at least one field has a non-empty array', () => {
|
|
501
|
+
const values = { email: [{ address: 'john.doe@cozycloud.cc' }], phone: [] }
|
|
502
|
+
const fields = [{ name: 'email' }, { name: 'phone' }]
|
|
503
|
+
|
|
504
|
+
const res = hasNoValues(values, fields)
|
|
505
|
+
|
|
506
|
+
expect(res).toBe(false)
|
|
507
|
+
})
|
|
508
|
+
|
|
509
|
+
it('should return true for nested field paths with no values', () => {
|
|
510
|
+
const values = { phone: [{ number: undefined }] }
|
|
511
|
+
const fields = [{ name: 'phone[0].number' }]
|
|
512
|
+
|
|
513
|
+
const res = hasNoValues(values, fields)
|
|
514
|
+
|
|
515
|
+
expect(res).toBe(true)
|
|
516
|
+
})
|
|
517
|
+
|
|
518
|
+
it('should handle mixed array and non-array values', () => {
|
|
519
|
+
const values = { firstname: 'John', lastname: ['Doe'] }
|
|
520
|
+
const fields = [{ name: 'firstname' }, { name: 'lastname' }]
|
|
521
|
+
|
|
522
|
+
const res = hasNoValues(values, fields)
|
|
523
|
+
|
|
524
|
+
expect(res).toBe(false)
|
|
525
|
+
})
|
|
526
|
+
|
|
527
|
+
it('should return true when fields array is empty', () => {
|
|
528
|
+
const values = { firstname: 'John', lastname: 'Doe' }
|
|
529
|
+
const fields = []
|
|
530
|
+
|
|
531
|
+
const res = hasNoValues(values, fields)
|
|
532
|
+
|
|
533
|
+
expect(res).toBe(true)
|
|
534
|
+
})
|
|
535
|
+
})
|
|
536
|
+
|
|
537
|
+
describe('getFirstValueIfArray', () => {
|
|
538
|
+
it('should return the first value of arrays', () => {
|
|
539
|
+
const values = {
|
|
540
|
+
phone: [{ phone: '123' }, { phone: '456' }],
|
|
541
|
+
email: [{ email: 'test@example.com' }],
|
|
542
|
+
name: 'John'
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
const res = getFirstValueIfArray(values)
|
|
546
|
+
|
|
547
|
+
expect(res).toEqual({
|
|
548
|
+
phone: { phone: '123' },
|
|
549
|
+
email: { email: 'test@example.com' },
|
|
550
|
+
name: 'John'
|
|
551
|
+
})
|
|
552
|
+
})
|
|
553
|
+
|
|
554
|
+
it('should return undefined for empty arrays', () => {
|
|
555
|
+
const values = {
|
|
556
|
+
phone: [],
|
|
557
|
+
email: [{ email: 'test@example.com' }]
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
const res = getFirstValueIfArray(values)
|
|
561
|
+
|
|
562
|
+
expect(res).toEqual({
|
|
563
|
+
phone: undefined,
|
|
564
|
+
email: { email: 'test@example.com' }
|
|
565
|
+
})
|
|
566
|
+
})
|
|
567
|
+
|
|
568
|
+
it('should not mutate the original object', () => {
|
|
569
|
+
const values = {
|
|
570
|
+
phone: [{ phone: '123' }, { phone: '456' }],
|
|
571
|
+
name: 'John'
|
|
572
|
+
}
|
|
573
|
+
const originalValues = JSON.parse(JSON.stringify(values))
|
|
574
|
+
|
|
575
|
+
getFirstValueIfArray(values)
|
|
576
|
+
|
|
577
|
+
expect(values).toEqual(originalValues)
|
|
578
|
+
})
|
|
579
|
+
|
|
580
|
+
it('should handle non-array values', () => {
|
|
581
|
+
const values = {
|
|
582
|
+
name: 'John',
|
|
583
|
+
age: 30,
|
|
584
|
+
active: true,
|
|
585
|
+
address: null
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
const res = getFirstValueIfArray(values)
|
|
589
|
+
|
|
590
|
+
expect(res).toEqual({
|
|
591
|
+
name: 'John',
|
|
592
|
+
age: 30,
|
|
593
|
+
active: true,
|
|
594
|
+
address: null
|
|
595
|
+
})
|
|
596
|
+
})
|
|
597
|
+
|
|
598
|
+
it('should handle mixed array and non-array values', () => {
|
|
599
|
+
const values = {
|
|
600
|
+
phone: [{ phone: '123' }],
|
|
601
|
+
name: 'John',
|
|
602
|
+
email: []
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
const res = getFirstValueIfArray(values)
|
|
606
|
+
|
|
607
|
+
expect(res).toEqual({
|
|
608
|
+
phone: { phone: '123' },
|
|
609
|
+
name: 'John',
|
|
610
|
+
email: undefined
|
|
611
|
+
})
|
|
612
|
+
})
|
|
613
|
+
|
|
614
|
+
it('should return empty object for empty input', () => {
|
|
615
|
+
const values = {}
|
|
616
|
+
|
|
617
|
+
const res = getFirstValueIfArray(values)
|
|
618
|
+
|
|
619
|
+
expect(res).toEqual({})
|
|
620
|
+
})
|
|
621
|
+
})
|
|
622
|
+
|
|
623
|
+
describe('validateFields', () => {
|
|
624
|
+
it('should return errors for required fields that are empty (non-array fields)', () => {
|
|
625
|
+
const values = {
|
|
626
|
+
name: '',
|
|
627
|
+
email: undefined
|
|
628
|
+
}
|
|
629
|
+
const fields = [
|
|
630
|
+
{ name: 'name', required: true },
|
|
631
|
+
{ name: 'email', required: true },
|
|
632
|
+
{ name: 'phone', required: false }
|
|
633
|
+
]
|
|
634
|
+
|
|
635
|
+
const res = validateFields(values, fields, t)
|
|
636
|
+
|
|
637
|
+
expect(res).toEqual({
|
|
638
|
+
name: 'All required fields must be filled in',
|
|
639
|
+
email: 'All required fields must be filled in'
|
|
640
|
+
})
|
|
641
|
+
})
|
|
642
|
+
|
|
643
|
+
it('should return errors for required array fields that are empty', () => {
|
|
644
|
+
const values = {
|
|
645
|
+
phone: []
|
|
646
|
+
}
|
|
647
|
+
const fields = [{ name: 'phone', required: true, layout: 'array' }]
|
|
648
|
+
|
|
649
|
+
const res = validateFields(values, fields, t)
|
|
650
|
+
|
|
651
|
+
expect(res).toEqual({
|
|
652
|
+
phone: [
|
|
653
|
+
{
|
|
654
|
+
phone: 'All required fields must be filled in'
|
|
655
|
+
}
|
|
656
|
+
]
|
|
657
|
+
})
|
|
658
|
+
})
|
|
659
|
+
|
|
660
|
+
it('should not return errors for required array fields with valid first element', () => {
|
|
661
|
+
const values = {
|
|
662
|
+
phone: [{ phone: '123456789' }]
|
|
663
|
+
}
|
|
664
|
+
const fields = [{ name: 'phone', required: true, layout: 'array' }]
|
|
665
|
+
|
|
666
|
+
const res = validateFields(values, fields, t)
|
|
667
|
+
|
|
668
|
+
expect(res).toEqual({})
|
|
669
|
+
})
|
|
670
|
+
|
|
671
|
+
it('should not return errors for non-required fields', () => {
|
|
672
|
+
const values = {
|
|
673
|
+
name: '',
|
|
674
|
+
phone: []
|
|
675
|
+
}
|
|
676
|
+
const fields = [
|
|
677
|
+
{ name: 'name', required: false },
|
|
678
|
+
{ name: 'phone', required: false, layout: 'array' }
|
|
679
|
+
]
|
|
680
|
+
|
|
681
|
+
const res = validateFields(values, fields, t)
|
|
682
|
+
|
|
683
|
+
expect(res).toEqual({})
|
|
684
|
+
})
|
|
685
|
+
|
|
686
|
+
it('should handle mixed array and non-array required fields', () => {
|
|
687
|
+
const values = {
|
|
688
|
+
name: '',
|
|
689
|
+
phone: []
|
|
690
|
+
}
|
|
691
|
+
const fields = [
|
|
692
|
+
{ name: 'name', required: true },
|
|
693
|
+
{ name: 'phone', required: true, layout: 'array' }
|
|
694
|
+
]
|
|
695
|
+
|
|
696
|
+
const res = validateFields(values, fields, t)
|
|
697
|
+
|
|
698
|
+
expect(res).toEqual({
|
|
699
|
+
name: 'All required fields must be filled in',
|
|
700
|
+
phone: [
|
|
701
|
+
{
|
|
702
|
+
phone: 'All required fields must be filled in'
|
|
703
|
+
}
|
|
704
|
+
]
|
|
705
|
+
})
|
|
706
|
+
})
|
|
707
|
+
|
|
708
|
+
it('should use getFirstValueIfArray to check only first element of arrays', () => {
|
|
709
|
+
const values = {
|
|
710
|
+
phone: [{ phone: '123' }, {}]
|
|
711
|
+
}
|
|
712
|
+
const fields = [{ name: 'phone', required: true, layout: 'array' }]
|
|
713
|
+
|
|
714
|
+
const res = validateFields(values, fields, t)
|
|
715
|
+
|
|
716
|
+
// Should not return error because first element has phone value
|
|
717
|
+
expect(res).toEqual({})
|
|
718
|
+
})
|
|
719
|
+
|
|
720
|
+
it('should return empty errors object when no required fields are empty', () => {
|
|
721
|
+
const values = {
|
|
722
|
+
name: 'John',
|
|
723
|
+
phone: [{ phone: '123456789' }]
|
|
724
|
+
}
|
|
725
|
+
const fields = [
|
|
726
|
+
{ name: 'name', required: true },
|
|
727
|
+
{ name: 'phone', required: true, layout: 'array' }
|
|
728
|
+
]
|
|
729
|
+
|
|
730
|
+
const res = validateFields(values, fields, t)
|
|
731
|
+
|
|
732
|
+
expect(res).toEqual({})
|
|
733
|
+
})
|
|
391
734
|
})
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import arrayMutators from 'final-form-arrays'
|
|
2
2
|
import React, { useState } from 'react'
|
|
3
3
|
import { Form } from 'react-final-form'
|
|
4
|
+
import { useI18n, useExtendI18n } from 'twake-i18n'
|
|
4
5
|
|
|
5
6
|
import { getHasManyItems } from 'cozy-client/dist/associations/HasMany'
|
|
6
7
|
import Button from 'cozy-ui/transpiled/react/Buttons'
|
|
7
|
-
import { useI18n, useExtendI18n } from 'cozy-ui/transpiled/react/providers/I18n'
|
|
8
8
|
|
|
9
9
|
import FieldInputLayout from './FieldInputLayout'
|
|
10
10
|
import contactToFormValues from './contactToFormValues'
|
|
11
11
|
import { fields as defaultFields } from './fieldsConfig'
|
|
12
12
|
import formValuesToContact from './formValuesToContact'
|
|
13
|
-
import { validateFields, makeFields } from './helpers'
|
|
13
|
+
import { validateFields, makeFields, hasNoValues } from './helpers'
|
|
14
14
|
import { locales } from './locales'
|
|
15
15
|
// import { fullContactPropTypes } from '../../ContactPropTypes' // !!
|
|
16
16
|
|
|
@@ -55,9 +55,14 @@ const ContactForm = ({ contacts, contact, customFieldsProps, onSubmit }) => {
|
|
|
55
55
|
makeCustomContactValues,
|
|
56
56
|
t
|
|
57
57
|
})}
|
|
58
|
-
validate={values => validateFields(values, t)}
|
|
59
|
-
onSubmit={formValues =>
|
|
60
|
-
|
|
58
|
+
validate={values => validateFields(values, _fields, t)}
|
|
59
|
+
onSubmit={formValues => {
|
|
60
|
+
// avoid creating an empty contact
|
|
61
|
+
if (hasNoValues(formValues, _fields)) {
|
|
62
|
+
return
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return onSubmit(
|
|
61
66
|
formValuesToContact({
|
|
62
67
|
formValues,
|
|
63
68
|
oldContact: contact,
|
|
@@ -65,7 +70,7 @@ const ContactForm = ({ contacts, contact, customFieldsProps, onSubmit }) => {
|
|
|
65
70
|
t
|
|
66
71
|
})
|
|
67
72
|
)
|
|
68
|
-
}
|
|
73
|
+
}}
|
|
69
74
|
render={({ handleSubmit, valid, submitFailed, errors }) => {
|
|
70
75
|
setSubmitContactForm(handleSubmit)
|
|
71
76
|
return (
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import PropTypes from 'prop-types'
|
|
2
2
|
import React, { useState } from 'react'
|
|
3
|
+
import { useI18n, useExtendI18n } from 'twake-i18n'
|
|
3
4
|
|
|
4
5
|
import Button from 'cozy-ui/transpiled/react/Buttons'
|
|
5
6
|
import { ConfirmDialog } from 'cozy-ui/transpiled/react/CozyDialogs'
|
|
@@ -8,7 +9,6 @@ import RadioGroup from 'cozy-ui/transpiled/react/RadioGroup'
|
|
|
8
9
|
import Radio from 'cozy-ui/transpiled/react/Radios'
|
|
9
10
|
import TextField from 'cozy-ui/transpiled/react/TextField'
|
|
10
11
|
import { useBreakpoints } from 'cozy-ui/transpiled/react/providers/Breakpoints'
|
|
11
|
-
import { useI18n, useExtendI18n } from 'cozy-ui/transpiled/react/providers/I18n'
|
|
12
12
|
|
|
13
13
|
import { locales } from './locales'
|
|
14
14
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import React, { useState } from 'react'
|
|
2
|
+
import { useI18n, useExtendI18n } from 'twake-i18n'
|
|
2
3
|
|
|
3
4
|
import Button from 'cozy-ui/transpiled/react/Buttons'
|
|
4
5
|
import { FixedDialog } from 'cozy-ui/transpiled/react/CozyDialogs'
|
|
5
6
|
import { useAlert } from 'cozy-ui/transpiled/react/providers/Alert'
|
|
6
|
-
import { useI18n, useExtendI18n } from 'cozy-ui/transpiled/react/providers/I18n'
|
|
7
7
|
|
|
8
8
|
import ContactForm, { getSubmitContactForm } from './ContactForm'
|
|
9
9
|
import { locales } from './locales'
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import classNames from 'classnames'
|
|
2
2
|
import React, { Component } from 'react'
|
|
3
|
+
import { translate } from 'twake-i18n'
|
|
3
4
|
|
|
4
5
|
import Icon from 'cozy-ui/transpiled/react/Icon'
|
|
5
6
|
import PlusIcon from 'cozy-ui/transpiled/react/Icons/Plus'
|
|
6
7
|
import Input from 'cozy-ui/transpiled/react/legacy/Input'
|
|
7
|
-
import { translate } from 'cozy-ui/transpiled/react/providers/I18n'
|
|
8
8
|
|
|
9
9
|
import styles from './styles.styl'
|
|
10
10
|
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import React, { useContext, useState } from 'react'
|
|
2
|
-
|
|
3
|
-
import { useI18n, useExtendI18n } from 'cozy-ui/transpiled/react/providers/I18n'
|
|
2
|
+
import { useI18n, useExtendI18n } from 'twake-i18n'
|
|
4
3
|
|
|
5
4
|
import { translatedDefaultSelectedGroup } from './helpers'
|
|
6
5
|
import { locales } from './locales'
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import PropTypes from 'prop-types'
|
|
2
2
|
import React from 'react'
|
|
3
|
+
import { useI18n, useExtendI18n } from 'twake-i18n'
|
|
3
4
|
|
|
4
5
|
import Button from 'cozy-ui/transpiled/react/Buttons'
|
|
5
6
|
import Icon from 'cozy-ui/transpiled/react/Icon'
|
|
6
7
|
import BottomSelectIcon from 'cozy-ui/transpiled/react/Icons/BottomSelect'
|
|
7
|
-
import { useI18n, useExtendI18n } from 'cozy-ui/transpiled/react/providers/I18n'
|
|
8
8
|
|
|
9
9
|
import { locales } from '../locales'
|
|
10
10
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import get from 'lodash/get'
|
|
2
|
+
import { useI18n, useExtendI18n } from 'twake-i18n'
|
|
2
3
|
|
|
3
4
|
import { useAlert } from 'cozy-ui/transpiled/react/providers/Alert'
|
|
4
|
-
import { useI18n, useExtendI18n } from 'cozy-ui/transpiled/react/providers/I18n'
|
|
5
5
|
|
|
6
6
|
import { useSelectedGroup } from './GroupsSelectProvider'
|
|
7
7
|
import { isExistingGroup } from './helpers'
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
|
+
import { useI18n, useExtendI18n } from 'twake-i18n'
|
|
2
3
|
|
|
3
4
|
import { ControlDefault } from 'cozy-ui/transpiled/react/SelectBox'
|
|
4
5
|
import { useBreakpoints } from 'cozy-ui/transpiled/react/providers/Breakpoints'
|
|
5
|
-
import { useI18n, useExtendI18n } from 'cozy-ui/transpiled/react/providers/I18n'
|
|
6
6
|
|
|
7
7
|
import { locales } from './locales'
|
|
8
8
|
import GroupsSelect from '../GroupsSelect/GroupsSelect'
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React, { useState, useRef } from 'react'
|
|
2
|
+
import { useI18n, useExtendI18n } from 'twake-i18n'
|
|
2
3
|
|
|
3
4
|
import { useClient, generateWebLink } from 'cozy-client'
|
|
4
5
|
import { CONTACTS_DOCTYPE } from 'cozy-client/dist/models/contact'
|
|
@@ -10,7 +11,6 @@ import Link from 'cozy-ui/transpiled/react/Link'
|
|
|
10
11
|
import ActionMenu, {
|
|
11
12
|
ActionMenuItem
|
|
12
13
|
} from 'cozy-ui/transpiled/react/deprecated/ActionMenu'
|
|
13
|
-
import { useI18n, useExtendI18n } from 'cozy-ui/transpiled/react/providers/I18n'
|
|
14
14
|
|
|
15
15
|
import { locales } from './locales'
|
|
16
16
|
import AppIcon from '../../AppIcon'
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import cx from 'classnames'
|
|
2
2
|
import PropTypes from 'prop-types'
|
|
3
3
|
import React from 'react'
|
|
4
|
+
import { useI18n, useExtendI18n } from 'twake-i18n'
|
|
4
5
|
|
|
5
6
|
import Button from 'cozy-ui/transpiled/react/Buttons'
|
|
6
7
|
import Icon from 'cozy-ui/transpiled/react/Icon'
|
|
7
8
|
import PersonAddIcon from 'cozy-ui/transpiled/react/Icons/PersonAdd'
|
|
8
9
|
import { useBreakpoints } from 'cozy-ui/transpiled/react/providers/Breakpoints'
|
|
9
|
-
import { useI18n, useExtendI18n } from 'cozy-ui/transpiled/react/providers/I18n'
|
|
10
10
|
|
|
11
11
|
import GroupsSelection from './GroupsSelection'
|
|
12
12
|
import ImportDropdown from './ImportDropdown'
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import PropTypes from 'prop-types'
|
|
2
2
|
import React from 'react'
|
|
3
|
+
import { useI18n, useExtendI18n } from 'twake-i18n'
|
|
3
4
|
|
|
4
5
|
import { getInitials } from 'cozy-client/dist/models/contact'
|
|
5
6
|
import Avatar from 'cozy-ui/transpiled/react/Avatar'
|
|
6
7
|
import { TableCell } from 'cozy-ui/transpiled/react/deprecated/Table'
|
|
7
|
-
import { useI18n, useExtendI18n } from 'cozy-ui/transpiled/react/providers/I18n'
|
|
8
8
|
|
|
9
9
|
import ContactName from './ContactName'
|
|
10
10
|
import { locales } from '../locales/withContactsListLocales'
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import PropTypes from 'prop-types'
|
|
2
2
|
import React from 'react'
|
|
3
|
+
import { useI18n } from 'twake-i18n'
|
|
3
4
|
|
|
4
5
|
import List from 'cozy-ui/transpiled/react/List'
|
|
5
6
|
import ListSubheader from 'cozy-ui/transpiled/react/ListSubheader'
|
|
6
7
|
import { Table } from 'cozy-ui/transpiled/react/deprecated/Table'
|
|
7
8
|
import useBreakpoints from 'cozy-ui/transpiled/react/providers/Breakpoints'
|
|
8
|
-
import { useI18n } from 'cozy-ui/transpiled/react/providers/I18n'
|
|
9
9
|
|
|
10
10
|
import ContactRow from './ContactRow'
|
|
11
11
|
import { sortContacts, categorizeContacts, sortHeaders } from './helpers'
|