cozy-ui-plus 4.0.0 → 4.2.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.
Files changed (26) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/Contacts/AddModal/ContactForm/FieldInput.js +3 -7
  3. package/dist/Contacts/AddModal/ContactForm/FieldInputArray.js +3 -5
  4. package/dist/Contacts/AddModal/ContactForm/FieldInputLayout.js +2 -4
  5. package/dist/Contacts/AddModal/ContactForm/FieldInputWrapper.js +1 -1
  6. package/dist/Contacts/AddModal/ContactForm/contactToFormValues.js +75 -90
  7. package/dist/Contacts/AddModal/ContactForm/helpers.js +104 -34
  8. package/dist/Contacts/AddModal/ContactForm/index.js +7 -2
  9. package/dist/Contacts/AddModal/ContactForm/locales/index.js +4 -4
  10. package/dist/src/Contacts/AddModal/ContactForm/FieldInput.d.ts +2 -6
  11. package/dist/src/Contacts/AddModal/ContactForm/FieldInputArray.d.ts +2 -6
  12. package/dist/src/Contacts/AddModal/ContactForm/helpers.d.ts +5 -2
  13. package/package.json +2 -2
  14. package/src/Contacts/AddModal/ContactForm/FieldInput.jsx +3 -7
  15. package/src/Contacts/AddModal/ContactForm/FieldInputArray.jsx +7 -4
  16. package/src/Contacts/AddModal/ContactForm/FieldInputLayout.jsx +3 -3
  17. package/src/Contacts/AddModal/ContactForm/FieldInputWrapper.jsx +1 -1
  18. package/src/Contacts/AddModal/ContactForm/contactToFormValues.js +79 -94
  19. package/src/Contacts/AddModal/ContactForm/contactToFormValues.spec.js +0 -23
  20. package/src/Contacts/AddModal/ContactForm/helpers.js +77 -17
  21. package/src/Contacts/AddModal/ContactForm/helpers.spec.js +344 -1
  22. package/src/Contacts/AddModal/ContactForm/index.jsx +24 -10
  23. package/src/Contacts/AddModal/ContactForm/locales/en.json +1 -1
  24. package/src/Contacts/AddModal/ContactForm/locales/fr.json +1 -1
  25. package/src/Contacts/AddModal/ContactForm/locales/ru.json +1 -1
  26. package/src/Contacts/AddModal/ContactForm/locales/vi.json +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cozy-ui-plus",
3
- "version": "4.0.0",
3
+ "version": "4.2.0",
4
4
  "description": "Cozy-ui-plus is an extension of cozy-ui and provides components based on cozy-client",
5
5
  "main": "dist/index.js",
6
6
  "license": "MIT",
@@ -74,5 +74,5 @@
74
74
  "react-dom": "^16 || ^17 || ^18",
75
75
  "twake-i18n": ">=0.3.0"
76
76
  },
77
- "gitHead": "b1d504a12eb88b8ecda4a53f82c400482d79452b"
77
+ "gitHead": "1fe28398fc8315ebb5eba401731f8e318c00f05a"
78
78
  }
@@ -22,8 +22,7 @@ const FieldInput = ({
22
22
  error,
23
23
  helperText,
24
24
  label,
25
- isInvisible,
26
- required
25
+ isInvisible
27
26
  }) => {
28
27
  const [id] = useState(uniqueId('field_')) // state only use to generate id once and not at each render
29
28
  const [hasBeenFocused, setHasBeenFocused] = useState(false)
@@ -57,7 +56,6 @@ const FieldInput = ({
57
56
  )}
58
57
  >
59
58
  <Field
60
- required={required}
61
59
  error={error}
62
60
  helperText={helperText}
63
61
  label={label}
@@ -111,13 +109,11 @@ FieldInput.propTypes = {
111
109
  }),
112
110
  // Destructuring props
113
111
  id: PropTypes.string,
114
- label: PropTypes.string,
115
- required: PropTypes.bool
112
+ label: PropTypes.string
116
113
  }
117
114
 
118
115
  FieldInput.defaultProps = {
119
- labelProps: null,
120
- required: false
116
+ labelProps: null
121
117
  }
122
118
 
123
119
  export default FieldInput
@@ -11,16 +11,17 @@ import PlusIcon from 'cozy-ui/transpiled/react/Icons/Plus'
11
11
  import ListItemIcon from 'cozy-ui/transpiled/react/ListItemIcon'
12
12
 
13
13
  import FieldInput from './FieldInput'
14
- import { fieldsRequired, addField, removeField } from './helpers'
14
+ import { addField, removeField, makeIsRequiredError } from './helpers'
15
15
  import { locales } from './locales'
16
16
 
17
17
  const FieldInputArray = ({
18
18
  attributes: { name, label, ...restAttributes },
19
19
  contacts,
20
- formProps: { valid, submitFailed, errors }
20
+ formProps
21
21
  }) => {
22
22
  useExtendI18n(locales)
23
23
  const { t } = useI18n()
24
+ const { errors } = formProps
24
25
 
25
26
  return (
26
27
  <FieldArray name={name}>
@@ -31,8 +32,10 @@ const FieldInputArray = ({
31
32
  const key = fields.value[index]?.fieldId || nameWithIndex
32
33
  const showRemove = fields.value[index]?.[name]
33
34
  const inputName = `${nameWithIndex}.${name}`
34
- const isError =
35
- fieldsRequired.includes(inputName) && !valid && submitFailed
35
+ const isError = makeIsRequiredError(
36
+ restAttributes.required,
37
+ formProps
38
+ )
36
39
 
37
40
  return (
38
41
  <div
@@ -8,7 +8,7 @@ import Icon from 'cozy-ui/transpiled/react/Icon'
8
8
  import FieldInput from './FieldInput'
9
9
  import FieldInputAccordion from './FieldInputAccordion'
10
10
  import FieldInputArray from './FieldInputArray'
11
- import { fieldsRequired } from './helpers'
11
+ import { makeIsRequiredError } from './helpers'
12
12
  import { locales } from './locales'
13
13
 
14
14
  const FieldInputLayout = ({
@@ -19,10 +19,10 @@ const FieldInputLayout = ({
19
19
  }) => {
20
20
  useExtendI18n(locales)
21
21
  const { t } = useI18n()
22
- const { valid, submitFailed, errors } = formProps
22
+ const { errors } = formProps
23
23
  const { name, label, ...restAttributes } = attributes
24
24
 
25
- const isError = fieldsRequired.includes(name) && !valid && submitFailed
25
+ const isError = makeIsRequiredError(restAttributes.required, formProps)
26
26
 
27
27
  return (
28
28
  <div
@@ -25,8 +25,8 @@ const FieldInputWrapper = ({
25
25
 
26
26
  return (
27
27
  <Component
28
- {...restAttributes}
29
28
  {...input}
29
+ {...restAttributes}
30
30
  {...props}
31
31
  variant={variant}
32
32
  fullWidth={fullWidth}
@@ -2,108 +2,93 @@ import uniqueId from 'lodash/uniqueId'
2
2
 
3
3
  import { getFormattedAddress } from 'cozy-client/dist/models/contact'
4
4
 
5
- import { fields } from './fieldsConfig'
6
5
  import { makeItemLabel, makeRelatedContact, movePrimaryToHead } from './helpers'
7
6
 
8
7
  const contactToFormValues = ({ contact, makeCustomContactValues, t }) => {
9
- // initialize the form values, required so that array fields start with at least one editable field
10
- const initialFieldValues = fields.reduce(
11
- (initialValues, { name, layout }) => {
12
- initialValues[name] = layout === 'array' ? [undefined] : undefined
13
- return initialValues
14
- },
15
- {}
16
- )
8
+ const {
9
+ gender,
10
+ address,
11
+ birthday,
12
+ birthplace,
13
+ company,
14
+ jobTitle,
15
+ cozy,
16
+ email,
17
+ impp,
18
+ name,
19
+ note,
20
+ phone
21
+ } = contact
17
22
 
18
- if (contact) {
19
- const {
20
- gender,
21
- address,
22
- birthday,
23
- birthplace,
24
- company,
25
- jobTitle,
26
- cozy,
27
- email,
28
- impp,
29
- name,
30
- note,
31
- phone
32
- } = contact
23
+ const addressValue =
24
+ address && address.length > 0
25
+ ? movePrimaryToHead(address).map(addressInfo => ({
26
+ fieldId: uniqueId('fieldId_'),
27
+ address: getFormattedAddress(addressInfo, t),
28
+ addressnumber: addressInfo.number,
29
+ addressstreet:
30
+ addressInfo.street || getFormattedAddress(addressInfo, t),
31
+ addresscode: addressInfo.postcode || addressInfo.code,
32
+ addresscity: addressInfo.city,
33
+ addressregion: addressInfo.region,
34
+ addresscountry: addressInfo.country,
35
+ addresslocality: addressInfo.extendedAddress?.locality,
36
+ addressbuilding: addressInfo.extendedAddress?.building,
37
+ addressstairs: addressInfo.extendedAddress?.stairs,
38
+ addressfloor: addressInfo.extendedAddress?.floor,
39
+ addressapartment: addressInfo.extendedAddress?.apartment,
40
+ addressentrycode: addressInfo.extendedAddress?.entrycode,
41
+ addressLabel: makeItemLabel(addressInfo)
42
+ }))
43
+ : [undefined]
44
+ const cozyValue = cozy && cozy.length > 0 ? cozy[0].url : undefined
45
+ const cozyLabel = cozy && cozy.length > 0 ? makeItemLabel(cozy[0]) : undefined
46
+ const emailValue =
47
+ email && email.length > 0
48
+ ? movePrimaryToHead(email).map(item => ({
49
+ fieldId: uniqueId('fieldId_'),
50
+ email: item?.address,
51
+ emailLabel: makeItemLabel(item)
52
+ }))
53
+ : [undefined]
54
+ const matrixValue =
55
+ impp && impp.length > 0
56
+ ? impp.find(item => item.label === 'work' && item.protocol === 'matrix')
57
+ ?.uri || undefined
58
+ : undefined
59
+ const phoneValue =
60
+ phone && phone.length > 0
61
+ ? movePrimaryToHead(phone).map(item => ({
62
+ fieldId: uniqueId('fieldId_'),
63
+ phone: item?.number,
64
+ phoneLabel: makeItemLabel(item)
65
+ }))
66
+ : [undefined]
33
67
 
34
- const addressValue =
35
- address && address.length > 0
36
- ? movePrimaryToHead(address).map(addressInfo => ({
37
- fieldId: uniqueId('fieldId_'),
38
- address: getFormattedAddress(addressInfo, t),
39
- addressnumber: addressInfo.number,
40
- addressstreet:
41
- addressInfo.street || getFormattedAddress(addressInfo, t),
42
- addresscode: addressInfo.postcode || addressInfo.code,
43
- addresscity: addressInfo.city,
44
- addressregion: addressInfo.region,
45
- addresscountry: addressInfo.country,
46
- addresslocality: addressInfo.extendedAddress?.locality,
47
- addressbuilding: addressInfo.extendedAddress?.building,
48
- addressstairs: addressInfo.extendedAddress?.stairs,
49
- addressfloor: addressInfo.extendedAddress?.floor,
50
- addressapartment: addressInfo.extendedAddress?.apartment,
51
- addressentrycode: addressInfo.extendedAddress?.entrycode,
52
- addressLabel: makeItemLabel(addressInfo)
53
- }))
54
- : [undefined]
55
- const cozyValue = cozy && cozy.length > 0 ? cozy[0].url : undefined
56
- const cozyLabel =
57
- cozy && cozy.length > 0 ? makeItemLabel(cozy[0]) : undefined
58
- const emailValue =
59
- email && email.length > 0
60
- ? movePrimaryToHead(email).map(item => ({
61
- fieldId: uniqueId('fieldId_'),
62
- email: item?.address,
63
- emailLabel: makeItemLabel(item)
64
- }))
65
- : [undefined]
66
- const matrixValue =
67
- impp && impp.length > 0
68
- ? impp.find(item => item.label === 'work' && item.protocol === 'matrix')
69
- ?.uri || undefined
70
- : undefined
71
- const phoneValue =
72
- phone && phone.length > 0
73
- ? movePrimaryToHead(phone).map(item => ({
74
- fieldId: uniqueId('fieldId_'),
75
- phone: item?.number,
76
- phoneLabel: makeItemLabel(item)
77
- }))
78
- : [undefined]
68
+ const relatedContactValue = makeRelatedContact(contact)
79
69
 
80
- const relatedContactValue = makeRelatedContact(contact)
70
+ const customValues = makeCustomContactValues?.(contact) || {}
81
71
 
82
- const customValues = makeCustomContactValues?.(contact) || {}
83
-
84
- return {
85
- ...customValues,
86
- gender,
87
- givenName: name?.givenName,
88
- additionalName: name?.additionalName,
89
- surname: name?.surname,
90
- familyName: name?.familyName,
91
- phone: phoneValue,
92
- email: emailValue,
93
- matrix: matrixValue,
94
- address: addressValue,
95
- cozy: cozyValue,
96
- cozyLabel: cozyLabel,
97
- company,
98
- jobTitle,
99
- birthday,
100
- birthplace,
101
- note,
102
- relatedContact: relatedContactValue
103
- }
72
+ return {
73
+ ...customValues,
74
+ gender,
75
+ givenName: name?.givenName,
76
+ additionalName: name?.additionalName,
77
+ surname: name?.surname,
78
+ familyName: name?.familyName,
79
+ phone: phoneValue,
80
+ email: emailValue,
81
+ matrix: matrixValue,
82
+ address: addressValue,
83
+ cozy: cozyValue,
84
+ cozyLabel: cozyLabel,
85
+ company,
86
+ jobTitle,
87
+ birthday,
88
+ birthplace,
89
+ note,
90
+ relatedContact: relatedContactValue
104
91
  }
105
-
106
- return initialFieldValues
107
92
  }
108
93
 
109
94
  export default contactToFormValues
@@ -5,29 +5,6 @@ import { johnDoeContact, johnDoeFormValues } from '../mocks'
5
5
  describe('contactToFormValues function', () => {
6
6
  const tSpy = jest.fn()
7
7
 
8
- it('should return default initial values', () => {
9
- const expected = {
10
- address: [undefined],
11
- birthday: undefined,
12
- birthplace: undefined,
13
- gender: undefined,
14
- company: undefined,
15
- jobTitle: undefined,
16
- cozy: undefined,
17
- cozyLabel: undefined,
18
- email: [undefined],
19
- matrix: undefined,
20
- familyName: undefined,
21
- givenName: undefined,
22
- note: undefined,
23
- phone: [undefined],
24
- relatedContact: [undefined]
25
- }
26
-
27
- const result = contactToFormValues({ contact: null, t: tSpy })
28
- expect(result).toEqual(expected)
29
- })
30
-
31
8
  it('should return initial values when a contact is given (edit)', () => {
32
9
  const expected = { ...johnDoeFormValues, relatedContact: [undefined] }
33
10
  tSpy.mockReturnValue(
@@ -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 fieldsRequired = [
13
- 'givenName',
14
- 'familyName',
15
- 'email[0].email',
16
- 'cozy'
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
+ }
19
+
20
+ /**
21
+ * Returns true if all fields have no values
22
+ * @param {object} values - Fields values
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
+ }
18
43
 
19
44
  /**
20
- * Returns errors if all required fields are empty
45
+ * Returns errors for required fields that are empty
21
46
  * @param {object} values - Fields values
22
- * @param {func} t - Translation function
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
- if (fieldsRequired.every(field => !get(values, field))) {
28
- fieldsRequired.forEach(field => {
29
- errors[field] = t('Contacts.AddModal.ContactForm.fields.required')
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
 
@@ -329,6 +366,18 @@ export const makeImppValues = (oldContact, value) => {
329
366
  )
330
367
  }
331
368
 
369
+ export const initializationFieldValues = fields => {
370
+ const initialFieldValues = fields.reduce(
371
+ (initialValues, { name, layout, defaultValue }) => {
372
+ initialValues[name] = layout === 'array' ? [undefined] : defaultValue
373
+ return initialValues
374
+ },
375
+ {}
376
+ )
377
+
378
+ return initialFieldValues
379
+ }
380
+
332
381
  /**
333
382
  *
334
383
  * @param {string} name
@@ -384,9 +433,20 @@ export const makeFields = (customFields, defaultFields) => {
384
433
 
385
434
  const fields = [...defaultFields]
386
435
 
387
- customFields.map(customField => {
388
- if (customField.position) {
389
- fields.splice(customField.position, 0, customField)
436
+ customFields.forEach(customField => {
437
+ const defaultField = fields.find(field => field.name === customField.name)
438
+ const _field = defaultField || customField
439
+
440
+ if (defaultField) {
441
+ Object.assign(_field, customField)
442
+ }
443
+
444
+ if (_field.position) {
445
+ if (defaultField) {
446
+ const fieldIndex = fields.indexOf(defaultField)
447
+ fields.splice(fieldIndex, 1)
448
+ }
449
+ fields.splice(_field.position, 0, _field)
390
450
  }
391
451
  })
392
452