cozy-ui-plus 4.0.0 → 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 +8 -0
- package/dist/Contacts/AddModal/ContactForm/FieldInput.js +3 -7
- package/dist/Contacts/AddModal/ContactForm/FieldInputArray.js +3 -5
- package/dist/Contacts/AddModal/ContactForm/FieldInputLayout.js +2 -4
- package/dist/Contacts/AddModal/ContactForm/FieldInputWrapper.js +1 -1
- package/dist/Contacts/AddModal/ContactForm/helpers.js +90 -33
- package/dist/Contacts/AddModal/ContactForm/index.js +6 -1
- package/dist/Contacts/AddModal/ContactForm/locales/index.js +4 -4
- 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/package.json +2 -2
- package/src/Contacts/AddModal/ContactForm/FieldInput.jsx +3 -7
- package/src/Contacts/AddModal/ContactForm/FieldInputArray.jsx +7 -4
- package/src/Contacts/AddModal/ContactForm/FieldInputLayout.jsx +3 -3
- package/src/Contacts/AddModal/ContactForm/FieldInputWrapper.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 +10 -5
- 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/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,14 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# [4.1.0](https://github.com/cozy/cozy-libs/compare/cozy-ui-plus@4.0.0...cozy-ui-plus@4.1.0) (2025-12-10)
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
- **ui-plus:** Add possibility to overrides fields in Contacts AddModal ([7224485](https://github.com/cozy/cozy-libs/commit/72244856a99c879d54b1317e402a095afcb8b3f6))
|
|
11
|
+
- **ui-plus:** Change required logic for Contacts AddModal ([cdd5650](https://github.com/cozy/cozy-libs/commit/cdd565070ddb98a515c63ee9f2026b4e2fa1532d))
|
|
12
|
+
- **ui-plus:** Contacts AddModal form input values are now overridable ([50e7502](https://github.com/cozy/cozy-libs/commit/50e7502e5143e0479941c8aeabe67e90796a735c))
|
|
13
|
+
|
|
6
14
|
# [4.0.0](https://github.com/cozy/cozy-libs/compare/cozy-ui-plus@3.1.2...cozy-ui-plus@4.0.0) (2025-12-04)
|
|
7
15
|
|
|
8
16
|
### Features
|
|
@@ -56,8 +56,7 @@ var FieldInput = function FieldInput(_ref) {
|
|
|
56
56
|
error = _ref.error,
|
|
57
57
|
helperText = _ref.helperText,
|
|
58
58
|
label = _ref.label,
|
|
59
|
-
isInvisible = _ref.isInvisible
|
|
60
|
-
required = _ref.required;
|
|
59
|
+
isInvisible = _ref.isInvisible;
|
|
61
60
|
|
|
62
61
|
var _useState = (0, _react.useState)((0, _uniqueId.default)('field_')),
|
|
63
62
|
_useState2 = (0, _slicedToArray2.default)(_useState, 1),
|
|
@@ -104,7 +103,6 @@ var FieldInput = function FieldInput(_ref) {
|
|
|
104
103
|
'u-dn': isInvisible
|
|
105
104
|
})
|
|
106
105
|
}, /*#__PURE__*/_react.default.createElement(_reactFinalForm.Field, {
|
|
107
|
-
required: required,
|
|
108
106
|
error: error,
|
|
109
107
|
helperText: helperText,
|
|
110
108
|
label: label,
|
|
@@ -153,12 +151,10 @@ FieldInput.propTypes = {
|
|
|
153
151
|
}),
|
|
154
152
|
// Destructuring props
|
|
155
153
|
id: _propTypes.default.string,
|
|
156
|
-
label: _propTypes.default.string
|
|
157
|
-
required: _propTypes.default.bool
|
|
154
|
+
label: _propTypes.default.string
|
|
158
155
|
};
|
|
159
156
|
FieldInput.defaultProps = {
|
|
160
|
-
labelProps: null
|
|
161
|
-
required: false
|
|
157
|
+
labelProps: null
|
|
162
158
|
};
|
|
163
159
|
var _default = FieldInput;
|
|
164
160
|
exports.default = _default;
|
|
@@ -43,15 +43,13 @@ var FieldInputArray = function FieldInputArray(_ref) {
|
|
|
43
43
|
label = _ref$attributes.label,
|
|
44
44
|
restAttributes = (0, _objectWithoutProperties2.default)(_ref$attributes, _excluded),
|
|
45
45
|
contacts = _ref.contacts,
|
|
46
|
-
|
|
47
|
-
valid = _ref$formProps.valid,
|
|
48
|
-
submitFailed = _ref$formProps.submitFailed,
|
|
49
|
-
errors = _ref$formProps.errors;
|
|
46
|
+
formProps = _ref.formProps;
|
|
50
47
|
(0, _twakeI18n.useExtendI18n)(_locales.locales);
|
|
51
48
|
|
|
52
49
|
var _useI18n = (0, _twakeI18n.useI18n)(),
|
|
53
50
|
t = _useI18n.t;
|
|
54
51
|
|
|
52
|
+
var errors = formProps.errors;
|
|
55
53
|
return /*#__PURE__*/_react.default.createElement(_reactFinalFormArrays.FieldArray, {
|
|
56
54
|
name: name
|
|
57
55
|
}, function (_ref2) {
|
|
@@ -62,7 +60,7 @@ var FieldInputArray = function FieldInputArray(_ref) {
|
|
|
62
60
|
var key = ((_fields$value$index = fields.value[index]) === null || _fields$value$index === void 0 ? void 0 : _fields$value$index.fieldId) || nameWithIndex;
|
|
63
61
|
var showRemove = (_fields$value$index2 = fields.value[index]) === null || _fields$value$index2 === void 0 ? void 0 : _fields$value$index2[name];
|
|
64
62
|
var inputName = "".concat(nameWithIndex, ".").concat(name);
|
|
65
|
-
var isError = _helpers.
|
|
63
|
+
var isError = (0, _helpers.makeIsRequiredError)(restAttributes.required, formProps);
|
|
66
64
|
return /*#__PURE__*/_react.default.createElement("div", {
|
|
67
65
|
key: key,
|
|
68
66
|
className: (0, _classnames.default)('u-flex u-flex-items-baseline', {
|
|
@@ -46,13 +46,11 @@ var FieldInputLayout = function FieldInputLayout(_ref) {
|
|
|
46
46
|
var _useI18n = (0, _twakeI18n.useI18n)(),
|
|
47
47
|
t = _useI18n.t;
|
|
48
48
|
|
|
49
|
-
var
|
|
50
|
-
submitFailed = formProps.submitFailed,
|
|
51
|
-
errors = formProps.errors;
|
|
49
|
+
var errors = formProps.errors;
|
|
52
50
|
var name = attributes.name,
|
|
53
51
|
label = attributes.label,
|
|
54
52
|
restAttributes = (0, _objectWithoutProperties2.default)(attributes, _excluded2);
|
|
55
|
-
var isError = _helpers.
|
|
53
|
+
var isError = (0, _helpers.makeIsRequiredError)(restAttributes.required, formProps);
|
|
56
54
|
return /*#__PURE__*/_react.default.createElement("div", {
|
|
57
55
|
className: (0, _classnames.default)('u-mt-1', {
|
|
58
56
|
'u-flex-items-center': !layout,
|
|
@@ -35,7 +35,7 @@ var FieldInputWrapper = function FieldInputWrapper(_ref) {
|
|
|
35
35
|
fullWidth = _ref.fullWidth,
|
|
36
36
|
props = (0, _objectWithoutProperties2.default)(_ref, _excluded2);
|
|
37
37
|
var Component = component || (restAttributes.customLabelOptions ? _TextFieldCustomLabelSelect.default : restAttributes !== null && restAttributes !== void 0 && restAttributes.select ? _TextFieldSelect.default : _TextField.default);
|
|
38
|
-
return /*#__PURE__*/_react.default.createElement(Component, (0, _extends2.default)({},
|
|
38
|
+
return /*#__PURE__*/_react.default.createElement(Component, (0, _extends2.default)({}, input, restAttributes, props, {
|
|
39
39
|
variant: variant,
|
|
40
40
|
fullWidth: fullWidth,
|
|
41
41
|
minRows: "2"
|
|
@@ -5,16 +5,16 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", {
|
|
6
6
|
value: true
|
|
7
7
|
});
|
|
8
|
-
exports.validateFields = exports.removeRelatedContactRelationships = exports.removeField = exports.removeAsscociatedData = exports.moveToHead = exports.movePrimaryToHead = exports.makeTypeAndLabel = exports.makeRelatedContact = exports.makeItemLabel = exports.makeInitialCustomValue = exports.makeImppValues = exports.makeFields = exports.makeCustomLabel = exports.hasExtendedAddress = exports.getRelatedContactRelationships = exports.
|
|
8
|
+
exports.validateFields = exports.removeRelatedContactRelationships = exports.removeField = exports.removeAsscociatedData = exports.moveToHead = exports.movePrimaryToHead = exports.makeTypeAndLabel = exports.makeRelatedContact = exports.makeItemLabel = exports.makeIsRequiredError = exports.makeInitialCustomValue = exports.makeImppValues = exports.makeFields = exports.makeCustomLabel = exports.hasNoValues = exports.hasExtendedAddress = exports.getRelatedContactRelationships = exports.getFirstValueIfArray = exports.createAddress = exports.addField = void 0;
|
|
9
9
|
|
|
10
10
|
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
|
|
11
11
|
|
|
12
|
-
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
13
|
-
|
|
14
12
|
var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties"));
|
|
15
13
|
|
|
16
14
|
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
|
|
17
15
|
|
|
16
|
+
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
17
|
+
|
|
18
18
|
var _get = _interopRequireDefault(require("lodash/get"));
|
|
19
19
|
|
|
20
20
|
var _isEqual = _interopRequireDefault(require("lodash/isEqual"));
|
|
@@ -35,27 +35,69 @@ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (O
|
|
|
35
35
|
|
|
36
36
|
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { (0, _defineProperty2.default)(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
|
37
37
|
|
|
38
|
-
var
|
|
38
|
+
var getFirstValueIfArray = function getFirstValueIfArray(values) {
|
|
39
|
+
return Object.keys(values).reduce(function (acc, key) {
|
|
40
|
+
var val = values[key];
|
|
41
|
+
acc[key] = Array.isArray(val) ? val[0] : val;
|
|
42
|
+
return acc;
|
|
43
|
+
}, {});
|
|
44
|
+
};
|
|
39
45
|
/**
|
|
40
|
-
* Returns
|
|
46
|
+
* Returns true if all fields have no values
|
|
41
47
|
* @param {object} values - Fields values
|
|
42
|
-
* @param {
|
|
48
|
+
* @param {import('../types').Field[]} fields - Fields configuration
|
|
49
|
+
* @returns {boolean} True if all fields have no values
|
|
50
|
+
*/
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
exports.getFirstValueIfArray = getFirstValueIfArray;
|
|
54
|
+
|
|
55
|
+
var hasNoValues = function hasNoValues(values, fields) {
|
|
56
|
+
var _values = getFirstValueIfArray(values);
|
|
57
|
+
|
|
58
|
+
return fields.every(function (field) {
|
|
59
|
+
return !(0, _get.default)(_values, field.name);
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* Returns true if the input is required and the form is invalid
|
|
64
|
+
* @param {boolean} required - Whether the input is required
|
|
65
|
+
* @param {object} formProps - The form props
|
|
66
|
+
* @returns {boolean} True if the input is required and the form is invalid
|
|
67
|
+
*/
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
exports.hasNoValues = hasNoValues;
|
|
71
|
+
|
|
72
|
+
var makeIsRequiredError = function makeIsRequiredError(required, formProps) {
|
|
73
|
+
var valid = formProps.valid,
|
|
74
|
+
submitFailed = formProps.submitFailed;
|
|
75
|
+
return required && !valid && submitFailed;
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* Returns errors for required fields that are empty
|
|
79
|
+
* @param {object} values - Fields values
|
|
80
|
+
* @param {import('../types').Field[]} fields - Fields configuration
|
|
81
|
+
* @param {Function} t - Translation function
|
|
43
82
|
* @returns {object} Errors
|
|
44
83
|
*/
|
|
45
84
|
|
|
46
|
-
exports.fieldsRequired = fieldsRequired;
|
|
47
85
|
|
|
48
|
-
|
|
86
|
+
exports.makeIsRequiredError = makeIsRequiredError;
|
|
87
|
+
|
|
88
|
+
var validateFields = function validateFields(values, fields, t) {
|
|
49
89
|
var errors = {};
|
|
90
|
+
var fieldsRequired = fields.filter(function (field) {
|
|
91
|
+
return field.required;
|
|
92
|
+
});
|
|
50
93
|
|
|
51
|
-
|
|
52
|
-
return !(0, _get.default)(values, field);
|
|
53
|
-
})) {
|
|
54
|
-
fieldsRequired.forEach(function (field) {
|
|
55
|
-
errors[field] = t('Contacts.AddModal.ContactForm.fields.required');
|
|
56
|
-
});
|
|
57
|
-
}
|
|
94
|
+
var _values = getFirstValueIfArray(values);
|
|
58
95
|
|
|
96
|
+
fieldsRequired.forEach(function (field) {
|
|
97
|
+
if (!(0, _get.default)(_values, field.name)) {
|
|
98
|
+
errors[field.name] = field.layout === 'array' ? [(0, _defineProperty2.default)({}, field.name, t('Contacts.AddModal.ContactForm.fields.required'))] : t('Contacts.AddModal.ContactForm.fields.required');
|
|
99
|
+
}
|
|
100
|
+
});
|
|
59
101
|
return errors;
|
|
60
102
|
};
|
|
61
103
|
/**
|
|
@@ -130,10 +172,10 @@ var movePrimaryToHead = moveToHead(function (v) {
|
|
|
130
172
|
});
|
|
131
173
|
exports.movePrimaryToHead = movePrimaryToHead;
|
|
132
174
|
|
|
133
|
-
var createAddress = function createAddress(
|
|
134
|
-
var address =
|
|
135
|
-
oldContact =
|
|
136
|
-
t =
|
|
175
|
+
var createAddress = function createAddress(_ref2) {
|
|
176
|
+
var address = _ref2.address,
|
|
177
|
+
oldContact = _ref2.oldContact,
|
|
178
|
+
t = _ref2.t;
|
|
137
179
|
return address ? address.filter(function (val) {
|
|
138
180
|
return val && val.address;
|
|
139
181
|
}).map(function (addressField, index) {
|
|
@@ -149,9 +191,9 @@ var createAddress = function createAddress(_ref) {
|
|
|
149
191
|
if (addressHasBeenModified) {
|
|
150
192
|
// Use "code" instead "postcode", to be vcard 4.0 rfc 6350 compliant
|
|
151
193
|
// eslint-disable-next-line no-unused-vars
|
|
152
|
-
var
|
|
153
|
-
postcode =
|
|
154
|
-
oldContactAddressCleaned = (0, _objectWithoutProperties2.default)(
|
|
194
|
+
var _ref3 = oldContactAddress || {},
|
|
195
|
+
postcode = _ref3.postcode,
|
|
196
|
+
oldContactAddressCleaned = (0, _objectWithoutProperties2.default)(_ref3, _excluded);
|
|
155
197
|
|
|
156
198
|
return _objectSpread(_objectSpread(_objectSpread(_objectSpread({}, oldContactAddressCleaned), {}, {
|
|
157
199
|
formattedAddress: addressField.address,
|
|
@@ -229,10 +271,10 @@ exports.getRelatedContactRelationships = getRelatedContactRelationships;
|
|
|
229
271
|
var removeRelatedContactRelationships = function removeRelatedContactRelationships(contact) {
|
|
230
272
|
if (!(contact !== null && contact !== void 0 && contact.relationships)) return contact;
|
|
231
273
|
var updatedContact = (0, _merge.default)({}, contact);
|
|
232
|
-
var relationshipsWithoutRelatedContact = Object.entries(updatedContact.relationships).reduce(function (acc,
|
|
233
|
-
var
|
|
234
|
-
relName =
|
|
235
|
-
relValue =
|
|
274
|
+
var relationshipsWithoutRelatedContact = Object.entries(updatedContact.relationships).reduce(function (acc, _ref4) {
|
|
275
|
+
var _ref5 = (0, _slicedToArray2.default)(_ref4, 2),
|
|
276
|
+
relName = _ref5[0],
|
|
277
|
+
relValue = _ref5[1];
|
|
236
278
|
|
|
237
279
|
if ('related' === relName) {
|
|
238
280
|
acc[relName] = relValue;
|
|
@@ -257,10 +299,10 @@ exports.removeRelatedContactRelationships = removeRelatedContactRelationships;
|
|
|
257
299
|
|
|
258
300
|
var removeAsscociatedData = function removeAsscociatedData(contact) {
|
|
259
301
|
if (!contact) return {};
|
|
260
|
-
return Object.entries(contact).reduce(function (cleanedContact,
|
|
261
|
-
var
|
|
262
|
-
key =
|
|
263
|
-
value =
|
|
302
|
+
return Object.entries(contact).reduce(function (cleanedContact, _ref6) {
|
|
303
|
+
var _ref7 = (0, _slicedToArray2.default)(_ref6, 2),
|
|
304
|
+
key = _ref7[0],
|
|
305
|
+
value = _ref7[1];
|
|
264
306
|
|
|
265
307
|
// Add `groups` condition to keep the old implementation functional, see below
|
|
266
308
|
if (!(value instanceof _cozyClient.Association) || key === 'groups') {
|
|
@@ -456,9 +498,24 @@ var makeFields = function makeFields(customFields, defaultFields) {
|
|
|
456
498
|
}
|
|
457
499
|
|
|
458
500
|
var fields = (0, _toConsumableArray2.default)(defaultFields);
|
|
459
|
-
customFields.
|
|
460
|
-
|
|
461
|
-
|
|
501
|
+
customFields.forEach(function (customField) {
|
|
502
|
+
var defaultField = fields.find(function (field) {
|
|
503
|
+
return field.name === customField.name;
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
var _field = defaultField || customField;
|
|
507
|
+
|
|
508
|
+
if (defaultField) {
|
|
509
|
+
Object.assign(_field, customField);
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
if (_field.position) {
|
|
513
|
+
if (defaultField) {
|
|
514
|
+
var fieldIndex = fields.indexOf(defaultField);
|
|
515
|
+
fields.splice(fieldIndex, 1);
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
fields.splice(_field.position, 0, _field);
|
|
462
519
|
}
|
|
463
520
|
});
|
|
464
521
|
return fields;
|
|
@@ -104,9 +104,14 @@ var ContactForm = function ContactForm(_ref) {
|
|
|
104
104
|
t: t
|
|
105
105
|
}),
|
|
106
106
|
validate: function validate(values) {
|
|
107
|
-
return (0, _helpers.validateFields)(values, t);
|
|
107
|
+
return (0, _helpers.validateFields)(values, _fields, t);
|
|
108
108
|
},
|
|
109
109
|
onSubmit: function onSubmit(formValues) {
|
|
110
|
+
// avoid creating an empty contact
|
|
111
|
+
if ((0, _helpers.hasNoValues)(formValues, _fields)) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
110
115
|
return _onSubmit((0, _formValuesToContact.default)({
|
|
111
116
|
formValues: formValues,
|
|
112
117
|
oldContact: contact,
|
|
@@ -32,7 +32,7 @@ var en = {
|
|
|
32
32
|
relationship: "Relationship",
|
|
33
33
|
note: "Notes",
|
|
34
34
|
label: "Label",
|
|
35
|
-
required: "
|
|
35
|
+
required: "All required fields must be filled in"
|
|
36
36
|
},
|
|
37
37
|
label: {
|
|
38
38
|
none: "None",
|
|
@@ -107,7 +107,7 @@ var fr = {
|
|
|
107
107
|
relationship: "Relation",
|
|
108
108
|
note: "Notes",
|
|
109
109
|
label: "Libell\xE9",
|
|
110
|
-
required: "
|
|
110
|
+
required: "Tous les champs requis doivent \xEAtre renseign\xE9s"
|
|
111
111
|
},
|
|
112
112
|
label: {
|
|
113
113
|
none: "Aucun",
|
|
@@ -182,7 +182,7 @@ var ru = {
|
|
|
182
182
|
relationship: "\u041E\u0442\u043D\u043E\u0448\u0435\u043D\u0438\u0435",
|
|
183
183
|
note: "\u0417\u0430\u043C\u0435\u0442\u043A\u0438",
|
|
184
184
|
label: "\u041C\u0435\u0442\u043A\u0430",
|
|
185
|
-
required: "\
|
|
185
|
+
required: "\u0412\u0441\u0435 \u043E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u044C\u043D\u044B\u0435 \u043F\u043E\u043B\u044F \u0434\u043E\u043B\u0436\u043D\u044B \u0431\u044B\u0442\u044C \u0437\u0430\u043F\u043E\u043B\u043D\u0435\u043D\u044B"
|
|
186
186
|
},
|
|
187
187
|
label: {
|
|
188
188
|
none: "\u041D\u0435\u0442",
|
|
@@ -257,7 +257,7 @@ var vi = {
|
|
|
257
257
|
relationship: "M\u1ED1i quan h\u1EC7",
|
|
258
258
|
note: "Ghi ch\xFA",
|
|
259
259
|
label: "Nh\xE3n",
|
|
260
|
-
required: "
|
|
260
|
+
required: "T\u1EA5t c\u1EA3 c\xE1c tr\u01B0\u1EDDng b\u1EAFt bu\u1ED9c ph\u1EA3i \u0111\u01B0\u1EE3c \u0111i\u1EC1n"
|
|
261
261
|
},
|
|
262
262
|
label: {
|
|
263
263
|
none: "Kh\xF4ng c\xF3",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export default FieldInput;
|
|
2
|
-
declare function FieldInput({ name, labelProps, className, attributes: { subFields, ...restAttributes }, contacts, error, helperText, label, isInvisible
|
|
2
|
+
declare function FieldInput({ name, labelProps, className, attributes: { subFields, ...restAttributes }, contacts, error, helperText, label, isInvisible }: {
|
|
3
3
|
name: any;
|
|
4
4
|
labelProps: any;
|
|
5
5
|
className: any;
|
|
@@ -12,7 +12,6 @@ declare function FieldInput({ name, labelProps, className, attributes: { subFiel
|
|
|
12
12
|
helperText: any;
|
|
13
13
|
label: any;
|
|
14
14
|
isInvisible: any;
|
|
15
|
-
required: any;
|
|
16
15
|
}): JSX.Element;
|
|
17
16
|
declare namespace FieldInput {
|
|
18
17
|
namespace propTypes {
|
|
@@ -26,12 +25,9 @@ declare namespace FieldInput {
|
|
|
26
25
|
}>>;
|
|
27
26
|
export let id: PropTypes.Requireable<string>;
|
|
28
27
|
export let label: PropTypes.Requireable<string>;
|
|
29
|
-
export let required: PropTypes.Requireable<boolean>;
|
|
30
28
|
}
|
|
31
29
|
namespace defaultProps {
|
|
32
|
-
|
|
33
|
-
let required_1: boolean;
|
|
34
|
-
export { required_1 as required };
|
|
30
|
+
let labelProps: any;
|
|
35
31
|
}
|
|
36
32
|
}
|
|
37
33
|
import PropTypes from 'prop-types';
|
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
export default FieldInputArray;
|
|
2
|
-
declare function FieldInputArray({ attributes: { name, label, ...restAttributes }, contacts, formProps
|
|
2
|
+
declare function FieldInputArray({ attributes: { name, label, ...restAttributes }, contacts, formProps }: {
|
|
3
3
|
attributes: {
|
|
4
4
|
[x: string]: any;
|
|
5
5
|
name: any;
|
|
6
6
|
label: any;
|
|
7
7
|
};
|
|
8
8
|
contacts: any;
|
|
9
|
-
formProps:
|
|
10
|
-
valid: any;
|
|
11
|
-
submitFailed: any;
|
|
12
|
-
errors: any;
|
|
13
|
-
};
|
|
9
|
+
formProps: any;
|
|
14
10
|
}): JSX.Element;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
export
|
|
2
|
-
export function
|
|
1
|
+
export function getFirstValueIfArray(values: any): {};
|
|
2
|
+
export function hasNoValues(values: object, fields: import("../types").Field[]): boolean;
|
|
3
|
+
export function makeIsRequiredError(required: boolean, formProps: object): boolean;
|
|
4
|
+
export function validateFields(values: object, fields: import("../types").Field[], t: Function): object;
|
|
3
5
|
export function makeItemLabel(item?: object): string;
|
|
4
6
|
export function makeTypeAndLabel(itemLabel?: string): {
|
|
5
7
|
type?: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cozy-ui-plus",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.1.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": "
|
|
77
|
+
"gitHead": "463474b57578fbca2e4fa04165e94abb82627d03"
|
|
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 {
|
|
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
|
|
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
|
-
|
|
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 {
|
|
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 {
|
|
22
|
+
const { errors } = formProps
|
|
23
23
|
const { name, label, ...restAttributes } = attributes
|
|
24
24
|
|
|
25
|
-
const isError =
|
|
25
|
+
const isError = makeIsRequiredError(restAttributes.required, formProps)
|
|
26
26
|
|
|
27
27
|
return (
|
|
28
28
|
<div
|
|
@@ -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
|
})
|
|
@@ -10,7 +10,7 @@ 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 (
|