@shopgate/pwa-ui-shared 7.30.0-alpha.6 → 7.30.0-alpha.8
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/AccordionContainer/index.js +39 -5
- package/AccordionContainer/spec.js +25 -2
- package/ActionButton/index.js +63 -7
- package/ActionButton/spec.js +59 -2
- package/ActionButton/style.js +22 -1
- package/AddToCartButton/index.js +184 -27
- package/AddToCartButton/mock.js +18 -4
- package/AddToCartButton/spec.js +51 -2
- package/AddToCartButton/style.js +127 -11
- package/Availability/index.js +34 -2
- package/Availability/spec.js +41 -1
- package/Availability/style.js +19 -1
- package/Button/index.js +76 -5
- package/Button/spec.js +33 -1
- package/Button/style.js +130 -21
- package/ButtonLink/connector.js +11 -2
- package/ButtonLink/index.js +44 -6
- package/ButtonLink/spec.js +23 -1
- package/Card/index.js +19 -2
- package/Card/style.js +11 -1
- package/CardList/components/Item/index.js +26 -2
- package/CardList/components/Item/style.js +7 -1
- package/CardList/index.js +34 -3
- package/CartTotalLine/components/Amount/index.js +28 -2
- package/CartTotalLine/components/Amount/style.js +8 -1
- package/CartTotalLine/components/Hint/index.js +23 -2
- package/CartTotalLine/components/Hint/style.js +12 -1
- package/CartTotalLine/components/Label/index.js +36 -2
- package/CartTotalLine/components/Label/style.js +17 -1
- package/CartTotalLine/components/Spacer/index.js +16 -2
- package/CartTotalLine/index.js +39 -2
- package/CartTotalLine/style.js +31 -1
- package/Checkbox/index.js +31 -2
- package/Checkbox/style.js +18 -1
- package/Chip/index.js +61 -2
- package/Chip/spec.js +24 -1
- package/Chip/style.js +71 -3
- package/ContextMenu/ContextMenu.hooks.js +6 -2
- package/ContextMenu/ContextMenuProvider.context.js +9 -3
- package/ContextMenu/ContextMenuProvider.js +21 -2
- package/ContextMenu/components/Item/index.js +67 -5
- package/ContextMenu/components/Item/style.js +32 -3
- package/ContextMenu/components/Position/index.js +61 -10
- package/ContextMenu/components/Position/style.js +11 -1
- package/ContextMenu/index.js +124 -3
- package/ContextMenu/spec.js +101 -2
- package/ContextMenu/style.js +45 -1
- package/Dialog/components/BasicDialog/index.js +5 -1
- package/Dialog/components/HtmlContentDialog/index.js +22 -2
- package/Dialog/components/HtmlContentDialog/spec.js +59 -1
- package/Dialog/components/PipelineErrorDialog/index.js +114 -25
- package/Dialog/components/PipelineErrorDialog/spec.js +92 -12
- package/Dialog/components/TextMessageDialog/index.js +28 -2
- package/Dialog/components/TextMessageDialog/spec.js +59 -1
- package/Dialog/components/VariantSelectModal/connector.js +11 -2
- package/Dialog/components/VariantSelectModal/index.js +65 -6
- package/Dialog/components/VariantSelectModal/spec.js +51 -2
- package/Dialog/constants.js +6 -1
- package/Dialog/index.js +114 -7
- package/Dialog/spec.js +81 -3
- package/DiscountBadge/index.js +30 -2
- package/DiscountBadge/spec.js +19 -1
- package/DiscountBadge/style.js +34 -2
- package/FavoritesButton/connector.js +18 -3
- package/FavoritesButton/index.js +118 -15
- package/FavoritesButton/mock.js +50 -4
- package/FavoritesButton/spec.js +120 -2
- package/FavoritesButton/style.js +26 -1
- package/Form/Builder/builders/buildCountryList.js +40 -6
- package/Form/Builder/builders/buildFormDefaults.js +35 -6
- package/Form/Builder/builders/buildFormElements.js +68 -10
- package/Form/Builder/builders/buildProvinceList.js +19 -2
- package/Form/Builder/builders/buildValidationErrorList.js +7 -2
- package/Form/Builder/classes/ActionListener/constants.js +22 -2
- package/Form/Builder/classes/ActionListener/index.js +441 -93
- package/Form/Builder/classes/ActionListener/spec.js +321 -19
- package/Form/Builder/components/CheckboxElement.js +35 -3
- package/Form/Builder/components/CountryElement.js +40 -3
- package/Form/Builder/components/ProvinceElement.js +40 -3
- package/Form/Builder/components/RadioElement.js +41 -3
- package/Form/Builder/components/SelectElement.js +39 -3
- package/Form/Builder/components/TextElement.js +49 -4
- package/Form/Builder/elementTypes.js +11 -1
- package/Form/Builder/index.js +298 -52
- package/Form/Builder/iso-3166-2.js +4943 -1
- package/Form/Builder/spec.js +300 -16
- package/Form/Checkbox/index.js +66 -4
- package/Form/Checkbox/style.js +25 -2
- package/Form/InfoField/index.js +50 -2
- package/Form/InfoField/spec.js +9 -1
- package/Form/InfoField/style.js +11 -1
- package/Form/Password/index.js +51 -6
- package/Form/Password/spec.js +34 -1
- package/Form/Password/style.js +11 -1
- package/Form/RadioGroup/components/Item/index.js +59 -3
- package/Form/RadioGroup/components/Item/style.js +32 -2
- package/Form/RadioGroup/index.js +101 -9
- package/Form/RadioGroup/spec.js +83 -3
- package/Form/RadioGroup/style.js +18 -2
- package/Form/Select/index.js +158 -10
- package/Form/Select/spec.js +36 -5
- package/Form/Select/style.js +27 -1
- package/Form/SelectContextChoices/index.js +77 -3
- package/Form/SelectContextChoices/spec.js +33 -4
- package/Form/SelectContextChoices/style.js +23 -1
- package/Form/TextField/index.js +92 -8
- package/Form/TextField/spec.js +110 -1
- package/Form/TextField/style.js +66 -8
- package/Form/index.js +54 -13
- package/FormElement/components/ErrorText/index.js +31 -2
- package/FormElement/components/ErrorText/style.js +13 -1
- package/FormElement/components/Label/index.js +35 -2
- package/FormElement/components/Label/style.js +76 -8
- package/FormElement/components/Placeholder/index.js +26 -2
- package/FormElement/components/Placeholder/style.js +48 -6
- package/FormElement/components/Underline/index.js +18 -2
- package/FormElement/components/Underline/style.js +51 -4
- package/FormElement/index.js +91 -6
- package/FormElement/spec.js +67 -2
- package/FormElement/style.js +13 -2
- package/Glow/index.js +90 -7
- package/Glow/spec.js +9 -1
- package/Glow/style.js +18 -1
- package/IndicatorCircle/index.js +33 -3
- package/IndicatorCircle/spec.js +28 -1
- package/IndicatorCircle/style.js +57 -3
- package/LoadingIndicator/index.js +29 -2
- package/LoadingIndicator/style.js +20 -1
- package/Manufacturer/index.js +20 -2
- package/Manufacturer/style.js +5 -1
- package/MessageBar/index.js +36 -2
- package/MessageBar/spec.js +79 -1
- package/MessageBar/style.js +38 -1
- package/NoResults/components/Icon/index.js +130 -2
- package/NoResults/components/Icon/style.js +17 -1
- package/NoResults/index.js +46 -2
- package/NoResults/style.js +31 -1
- package/Placeholder/index.js +25 -3
- package/Placeholder/style.js +11 -1
- package/PlaceholderLabel/index.js +27 -2
- package/PlaceholderLabel/spec.js +19 -1
- package/PlaceholderLabel/style.js +12 -1
- package/PlaceholderParagraph/index.js +36 -2
- package/PlaceholderParagraph/spec.js +19 -1
- package/Price/index.js +88 -7
- package/Price/style.js +22 -1
- package/PriceInfo/index.js +20 -2
- package/PriceInfo/style.js +5 -1
- package/PriceStriked/index.js +83 -12
- package/PriceStriked/style.js +33 -3
- package/ProductProperties/index.js +32 -2
- package/ProgressBar/index.js +101 -13
- package/ProgressBar/spec.js +13 -1
- package/ProgressBar/style.js +83 -2
- package/RadioButton/index.js +18 -2
- package/RadioButton/spec.js +21 -1
- package/RadioButton/style.js +21 -1
- package/RatingNumber/index.js +29 -2
- package/RatingStars/constants.js +2 -1
- package/RatingStars/index.js +130 -12
- package/RatingStars/spec.js +90 -3
- package/RatingStars/style.js +51 -2
- package/Ripple/components/RippleAnimation/index.js +88 -6
- package/Ripple/index.js +218 -40
- package/Ripple/style.js +18 -1
- package/RippleButton/index.js +52 -5
- package/RippleButton/spec.js +45 -1
- package/ScannerOverlay/components/CameraOverlay/index.js +13 -2
- package/ScannerOverlay/components/CameraOverlay/style.js +41 -1
- package/ScannerOverlay/components/ScannerBar/components/FlashlightButton/index.js +34 -2
- package/ScannerOverlay/components/ScannerBar/components/FlashlightButton/style.js +28 -1
- package/ScannerOverlay/components/ScannerBar/components/ScannerInstructions/index.js +11 -2
- package/ScannerOverlay/components/ScannerBar/index.js +31 -2
- package/ScannerOverlay/components/ScannerBar/style.js +20 -1
- package/ScannerOverlay/index.js +47 -7
- package/Sheet/components/Header/components/SearchBar/index.js +46 -2
- package/Sheet/components/Header/components/SearchBar/spec.js +21 -3
- package/Sheet/components/Header/components/SearchBar/style.js +47 -1
- package/Sheet/components/Header/index.js +75 -7
- package/Sheet/components/Header/spec.js +14 -1
- package/Sheet/components/Header/style.js +50 -1
- package/Sheet/index.js +170 -17
- package/Sheet/spec.js +85 -5
- package/Sheet/style.js +143 -2
- package/TaxDisclaimer/index.js +34 -4
- package/TaxDisclaimer/spec.js +31 -3
- package/TaxDisclaimer/style.js +9 -1
- package/TextField/components/ErrorText/index.js +33 -2
- package/TextField/components/ErrorText/style.js +25 -3
- package/TextField/components/FormElement/index.js +19 -2
- package/TextField/components/FormElement/style.js +32 -4
- package/TextField/components/Hint/index.js +21 -2
- package/TextField/components/Hint/style.js +40 -5
- package/TextField/components/Label/index.js +32 -3
- package/TextField/components/Label/style.js +68 -8
- package/TextField/components/Underline/index.js +19 -2
- package/TextField/components/Underline/style.js +51 -4
- package/TextField/index.js +189 -27
- package/TextField/spec.js +128 -3
- package/TextField/style.js +34 -4
- package/ToggleIcon/index.js +58 -8
- package/ToggleIcon/spec.js +35 -1
- package/icons/AccountBoxIcon.js +11 -2
- package/icons/AddMoreIcon.js +11 -2
- package/icons/ArrowDropIcon.js +11 -2
- package/icons/ArrowIcon.js +21 -2
- package/icons/BarcodeScannerIcon.js +11 -2
- package/icons/BoxIcon.js +11 -2
- package/icons/BrowseIcon.js +11 -2
- package/icons/BurgerIcon.js +11 -2
- package/icons/CalendarIcon.js +15 -3
- package/icons/CartCouponIcon.js +72 -2
- package/icons/CartIcon.js +11 -2
- package/icons/CartPlusIcon.js +11 -2
- package/icons/CheckIcon.js +11 -2
- package/icons/CheckedIcon.js +11 -2
- package/icons/ChevronIcon.js +11 -2
- package/icons/CreditCardIcon.js +11 -2
- package/icons/CrossIcon.js +11 -2
- package/icons/DescriptionIcon.js +11 -2
- package/icons/FilterIcon.js +11 -2
- package/icons/FlashDisabledIcon.js +11 -2
- package/icons/FlashEnabledIcon.js +11 -2
- package/icons/GridIcon.js +11 -2
- package/icons/HeartIcon.js +11 -2
- package/icons/HeartOutlineIcon.js +11 -2
- package/icons/HeartPlusIcon.js +12 -2
- package/icons/HeartPlusOutlineIcon.js +12 -2
- package/icons/HomeIcon.js +11 -2
- package/icons/InfoIcon.js +11 -2
- package/icons/InfoOutlineIcon.js +11 -2
- package/icons/ListIcon.js +11 -2
- package/icons/LocalShippingIcon.js +11 -2
- package/icons/LocationIcon.js +13 -3
- package/icons/LocatorIcon.js +11 -2
- package/icons/LockIcon.js +11 -2
- package/icons/LogoutIcon.js +11 -2
- package/icons/MagnifierIcon.js +11 -2
- package/icons/MapMarkerIcon.js +24 -3
- package/icons/MoreIcon.js +11 -2
- package/icons/MoreVertIcon.js +11 -2
- package/icons/NotificationIcon.js +14 -3
- package/icons/PersonIcon.js +12 -2
- package/icons/PhoneIcon.js +13 -3
- package/icons/PlaceholderIcon.js +11 -2
- package/icons/RadioCheckedIcon.js +11 -2
- package/icons/RadioUncheckedIcon.js +11 -2
- package/icons/SecurityIcon.js +11 -2
- package/icons/ShippingMethodIcon.js +18 -3
- package/icons/ShoppingCartIcon.js +11 -2
- package/icons/SortIcon.js +11 -2
- package/icons/StarHalfIcon.js +18 -2
- package/icons/StarIcon.js +18 -2
- package/icons/StarOutlineIcon.js +11 -2
- package/icons/StopIcon.js +11 -2
- package/icons/TickIcon.js +11 -2
- package/icons/TimeIcon.js +14 -3
- package/icons/TrashIcon.js +11 -2
- package/icons/TrashOutlineIcon.js +12 -2
- package/icons/UncheckedIcon.js +11 -2
- package/icons/ViewListIcon.js +11 -2
- package/icons/VisibilityIcon.js +11 -2
- package/icons/VisibilityOffIcon.js +11 -2
- package/icons/WarningIcon.js +11 -2
- package/index.js +13 -1
- package/package.json +5 -5
|
@@ -1,6 +1,51 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import React, { PureComponent } from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import TextField from "../../../TextField";
|
|
4
|
+
import { ELEMENT_TYPE_TEXT, ELEMENT_TYPE_NUMBER, ELEMENT_TYPE_EMAIL, ELEMENT_TYPE_PASSWORD, ELEMENT_TYPE_DATE, ELEMENT_TYPE_PHONE } from "../elementTypes";
|
|
5
|
+
|
|
6
|
+
// Map element type to input type
|
|
7
|
+
const mapping = {
|
|
8
|
+
[ELEMENT_TYPE_TEXT]: 'text',
|
|
9
|
+
[ELEMENT_TYPE_NUMBER]: 'number',
|
|
10
|
+
[ELEMENT_TYPE_EMAIL]: 'email',
|
|
11
|
+
[ELEMENT_TYPE_PASSWORD]: 'password',
|
|
12
|
+
[ELEMENT_TYPE_DATE]: 'date',
|
|
13
|
+
[ELEMENT_TYPE_PHONE]: 'tel'
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
3
17
|
* React component that takes the element and additional data and renders a configured text input.
|
|
4
|
-
*/
|
|
18
|
+
*/
|
|
19
|
+
class TextElement extends PureComponent {
|
|
20
|
+
/**
|
|
5
21
|
* @returns {JSX}
|
|
6
|
-
*/
|
|
22
|
+
*/
|
|
23
|
+
render() {
|
|
24
|
+
const {
|
|
25
|
+
element,
|
|
26
|
+
errorText,
|
|
27
|
+
name,
|
|
28
|
+
style,
|
|
29
|
+
value
|
|
30
|
+
} = this.props;
|
|
31
|
+
const type = mapping[element.type];
|
|
32
|
+
return /*#__PURE__*/React.createElement(TextField, {
|
|
33
|
+
type: type,
|
|
34
|
+
name: name,
|
|
35
|
+
className: style.fields,
|
|
36
|
+
label: element.label,
|
|
37
|
+
value: value,
|
|
38
|
+
onChange: element.handleChange,
|
|
39
|
+
errorText: errorText,
|
|
40
|
+
isControlled: true,
|
|
41
|
+
translateErrorText: false
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
TextElement.defaultProps = {
|
|
46
|
+
value: '',
|
|
47
|
+
style: {
|
|
48
|
+
field: ''
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
export default TextElement;
|
|
@@ -1 +1,11 @@
|
|
|
1
|
-
export
|
|
1
|
+
export const ELEMENT_TYPE_EMAIL = 'email';
|
|
2
|
+
export const ELEMENT_TYPE_PASSWORD = 'password';
|
|
3
|
+
export const ELEMENT_TYPE_TEXT = 'text';
|
|
4
|
+
export const ELEMENT_TYPE_NUMBER = 'number';
|
|
5
|
+
export const ELEMENT_TYPE_SELECT = 'select';
|
|
6
|
+
export const ELEMENT_TYPE_COUNTRY = 'country';
|
|
7
|
+
export const ELEMENT_TYPE_PROVINCE = 'province';
|
|
8
|
+
export const ELEMENT_TYPE_CHECKBOX = 'checkbox';
|
|
9
|
+
export const ELEMENT_TYPE_RADIO = 'radio';
|
|
10
|
+
export const ELEMENT_TYPE_DATE = 'date';
|
|
11
|
+
export const ELEMENT_TYPE_PHONE = 'phone';
|
package/Form/Builder/index.js
CHANGED
|
@@ -1,64 +1,310 @@
|
|
|
1
|
-
import _isEqual from"lodash/isEqual";
|
|
1
|
+
import _isEqual from "lodash/isEqual";
|
|
2
|
+
import "core-js/modules/es.string.replace.js";
|
|
3
|
+
import React, { Component, Fragment } from 'react';
|
|
4
|
+
import PropTypes from 'prop-types';
|
|
5
|
+
import { logger } from '@shopgate/pwa-core/helpers';
|
|
6
|
+
import Portal from '@shopgate/pwa-common/components/Portal';
|
|
7
|
+
import Form from '@shopgate/pwa-ui-shared/Form';
|
|
8
|
+
import { BEFORE, AFTER } from '@shopgate/pwa-common/constants/Portals';
|
|
9
|
+
import ActionListener from "./classes/ActionListener";
|
|
10
|
+
import { ELEMENT_TYPE_EMAIL, ELEMENT_TYPE_PASSWORD, ELEMENT_TYPE_TEXT, ELEMENT_TYPE_NUMBER, ELEMENT_TYPE_SELECT, ELEMENT_TYPE_COUNTRY, ELEMENT_TYPE_PROVINCE, ELEMENT_TYPE_CHECKBOX, ELEMENT_TYPE_RADIO, ELEMENT_TYPE_DATE, ELEMENT_TYPE_PHONE } from "./elementTypes";
|
|
11
|
+
import TextElement from "./components/TextElement";
|
|
12
|
+
import SelectElement from "./components/SelectElement";
|
|
13
|
+
import CountryElement from "./components/CountryElement";
|
|
14
|
+
import ProvinceElement from "./components/ProvinceElement";
|
|
15
|
+
import RadioElement from "./components/RadioElement";
|
|
16
|
+
import CheckboxElement from "./components/CheckboxElement";
|
|
17
|
+
import buildFormElements from "./builders/buildFormElements";
|
|
18
|
+
import buildFormDefaults from "./builders/buildFormDefaults";
|
|
19
|
+
import buildCountryList from "./builders/buildCountryList";
|
|
20
|
+
import buildProvinceList from "./builders/buildProvinceList";
|
|
21
|
+
import buildValidationErrorList from "./builders/buildValidationErrorList";
|
|
22
|
+
|
|
23
|
+
/**
|
|
2
24
|
* Takes a string and converts it to a part to be used in a portal name
|
|
3
25
|
* @package FormBuilder
|
|
4
26
|
* @param {string} s The string to be sanitized
|
|
5
27
|
* @return {string}
|
|
6
|
-
*/
|
|
28
|
+
*/
|
|
29
|
+
const sanitize = s => s.replace(/[\\._]/, '-');
|
|
30
|
+
/**
|
|
7
31
|
* Optional select element
|
|
8
32
|
* @type {Object}
|
|
9
|
-
*/
|
|
33
|
+
*/
|
|
34
|
+
const emptySelectOption = {
|
|
35
|
+
'': ''
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
10
39
|
* Takes a form configuration and handles rendering and updates of the form fields.
|
|
11
40
|
* Note: Only one country and one province element is supported per FormBuilder instance.
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
*
|
|
16
|
-
* @
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*/
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
41
|
+
*/
|
|
42
|
+
class Builder extends Component {
|
|
43
|
+
/**
|
|
44
|
+
* Initializes the component.
|
|
45
|
+
* @param {Object} props The components props.
|
|
46
|
+
*/
|
|
47
|
+
constructor(props) {
|
|
48
|
+
super(props);
|
|
49
|
+
|
|
50
|
+
// Prepare internal state
|
|
51
|
+
/**
|
|
52
|
+
* Sorts the elements by "sortOrder" property
|
|
53
|
+
*
|
|
54
|
+
* @typedef {Object} FormElement
|
|
55
|
+
* @property {number} sortOrder
|
|
56
|
+
*
|
|
57
|
+
* @param {FormElement} element1 First element
|
|
58
|
+
* @param {FormElement} element2 Second element
|
|
59
|
+
* @returns {number}
|
|
60
|
+
*/
|
|
61
|
+
this.elementSortFunc = (element1, element2) => {
|
|
62
|
+
// Keep current sort order when no specific sort order was set for both
|
|
63
|
+
if (element1.sortOrder === undefined || element2.sortOrder === undefined) {
|
|
64
|
+
return 0;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Sort in ascending order of sortOrder otherwise
|
|
68
|
+
return element1.sortOrder - element2.sortOrder;
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* Element change handler based on it's type,
|
|
72
|
+
* @param {string} elementId Element to create the handler for
|
|
73
|
+
* @param {string} value Element value
|
|
74
|
+
*/
|
|
75
|
+
this.elementChangeHandler = (elementId, value) => {
|
|
76
|
+
// Apply value change to new state
|
|
77
|
+
const newState = {
|
|
78
|
+
...this.state,
|
|
79
|
+
formData: {
|
|
80
|
+
...this.state.formData,
|
|
81
|
+
[elementId]: value
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
// Remove validation error message on first change of the element
|
|
86
|
+
Object.keys(newState.errors).forEach(key => {
|
|
87
|
+
// Action listeners might add some again
|
|
88
|
+
if (this.state.formData[key] !== newState.formData[key]) {
|
|
89
|
+
delete newState.errors[key];
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
const hasBackendValidationErrors = Object.keys(newState.errors).length > 0;
|
|
93
|
+
|
|
94
|
+
// Handle context sensitive functionality by via "action" listener and use the "new" state
|
|
95
|
+
const updatedState = this.actionListener.notify(elementId, this.state, newState);
|
|
96
|
+
|
|
97
|
+
// TODO: handle frontend validation errors and set "hasErrors" accordingly
|
|
98
|
+
let hasErrors = false;
|
|
99
|
+
|
|
100
|
+
// Check "required" fields for all visible elements and enable rendering on changes
|
|
101
|
+
this.formElements.forEach(formElement => {
|
|
102
|
+
if (!updatedState.elementVisibility[formElement.id] || !formElement.required) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const tmpVal = updatedState.formData[formElement.id];
|
|
106
|
+
const tmpResult = tmpVal === null || tmpVal === undefined || tmpVal === '' || tmpVal === false;
|
|
107
|
+
hasErrors = hasErrors || tmpResult;
|
|
108
|
+
});
|
|
109
|
+
const hasFrontendValidationErrors = Object.keys(updatedState.errors).length <= 0;
|
|
110
|
+
const hasValidationErrors = hasBackendValidationErrors && hasFrontendValidationErrors;
|
|
111
|
+
|
|
112
|
+
// Handle state internally and send an "onChange" event to parent if this finished
|
|
113
|
+
this.setState(updatedState);
|
|
114
|
+
|
|
115
|
+
// Transform to external structure (unavailable ones will be set undefined)
|
|
116
|
+
const updateData = {};
|
|
117
|
+
this.formElements.forEach(el => {
|
|
118
|
+
if (el.custom) {
|
|
119
|
+
if (updateData.customAttributes === undefined) {
|
|
120
|
+
updateData.customAttributes = {};
|
|
121
|
+
}
|
|
122
|
+
updateData.customAttributes[el.id] = updatedState.formData[el.id];
|
|
123
|
+
} else {
|
|
124
|
+
updateData[el.id] = updatedState.formData[el.id];
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
// Trigger the given update action
|
|
129
|
+
this.props.handleUpdate(updateData, hasErrors || hasValidationErrors);
|
|
130
|
+
};
|
|
131
|
+
/**
|
|
132
|
+
* Takes an element of any type and renders it depending on type.
|
|
133
|
+
* Also puts portals around the element.
|
|
134
|
+
* @param {Object} element The data of the element to be rendered
|
|
135
|
+
* @returns {JSX}
|
|
136
|
+
*/
|
|
137
|
+
this.renderElement = element => {
|
|
138
|
+
const elementName = `${this.props.name}.${element.id}`;
|
|
139
|
+
const elementErrorText = this.state.errors[element.id] || '';
|
|
140
|
+
const elementValue = this.state.formData[element.id];
|
|
141
|
+
const elementVisible = this.state.elementVisibility[element.id] || false;
|
|
142
|
+
if (!elementVisible) {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/*
|
|
147
|
+
* Elements do check the type before they render themselves, but not even trying to render
|
|
148
|
+
* creates a better React DOM
|
|
149
|
+
*/
|
|
150
|
+
switch (element.type) {
|
|
151
|
+
case ELEMENT_TYPE_TEXT:
|
|
152
|
+
case ELEMENT_TYPE_NUMBER:
|
|
153
|
+
case ELEMENT_TYPE_EMAIL:
|
|
154
|
+
case ELEMENT_TYPE_PASSWORD:
|
|
155
|
+
case ELEMENT_TYPE_DATE:
|
|
156
|
+
case ELEMENT_TYPE_PHONE:
|
|
157
|
+
{
|
|
158
|
+
return /*#__PURE__*/React.createElement(TextElement, {
|
|
159
|
+
name: elementName,
|
|
160
|
+
element: element,
|
|
161
|
+
errorText: elementErrorText,
|
|
162
|
+
value: elementValue || '',
|
|
163
|
+
visible: elementVisible
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
case ELEMENT_TYPE_SELECT:
|
|
167
|
+
return /*#__PURE__*/React.createElement(SelectElement, {
|
|
168
|
+
name: elementName,
|
|
169
|
+
element: element,
|
|
170
|
+
errorText: elementErrorText,
|
|
171
|
+
value: elementValue,
|
|
172
|
+
visible: elementVisible
|
|
173
|
+
});
|
|
174
|
+
case ELEMENT_TYPE_COUNTRY:
|
|
175
|
+
return /*#__PURE__*/React.createElement(CountryElement, {
|
|
176
|
+
name: elementName,
|
|
177
|
+
element: element,
|
|
178
|
+
errorText: elementErrorText,
|
|
179
|
+
value: elementValue,
|
|
180
|
+
visible: elementVisible,
|
|
181
|
+
countryList: this.countryList
|
|
182
|
+
});
|
|
183
|
+
case ELEMENT_TYPE_PROVINCE:
|
|
184
|
+
{
|
|
185
|
+
const countryElement = this.formElements.find(el => el.type === ELEMENT_TYPE_COUNTRY);
|
|
186
|
+
const provincesList = countryElement && this.state.formData[countryElement.id] ? buildProvinceList(this.state.formData[countryElement.id], element.required ? null : emptySelectOption) : {};
|
|
187
|
+
return /*#__PURE__*/React.createElement(ProvinceElement, {
|
|
188
|
+
name: elementName,
|
|
189
|
+
element: element,
|
|
190
|
+
errorText: elementErrorText,
|
|
191
|
+
value: elementValue,
|
|
192
|
+
visible: elementVisible,
|
|
193
|
+
provincesList: provincesList
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
case ELEMENT_TYPE_RADIO:
|
|
197
|
+
{
|
|
198
|
+
return /*#__PURE__*/React.createElement(RadioElement, {
|
|
199
|
+
name: elementName,
|
|
200
|
+
element: element,
|
|
201
|
+
errorText: elementErrorText,
|
|
202
|
+
value: elementValue,
|
|
203
|
+
visible: elementVisible
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
case ELEMENT_TYPE_CHECKBOX:
|
|
207
|
+
{
|
|
208
|
+
return /*#__PURE__*/React.createElement(CheckboxElement, {
|
|
209
|
+
name: elementName,
|
|
210
|
+
element: element,
|
|
211
|
+
errorText: elementErrorText,
|
|
212
|
+
value: elementValue,
|
|
213
|
+
visible: elementVisible
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
default:
|
|
217
|
+
{
|
|
218
|
+
logger.error(`Unknown form element type: ${element.type}`);
|
|
219
|
+
break;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return null;
|
|
223
|
+
};
|
|
224
|
+
this.state = {
|
|
225
|
+
elementVisibility: {},
|
|
226
|
+
formData: {},
|
|
227
|
+
// Convert errors structure to direct access errors
|
|
228
|
+
errors: buildValidationErrorList(props.validationErrors)
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
// Reorganize form elements into a structure that can be easily rendered
|
|
232
|
+
const formElements = buildFormElements(props.config, this.elementChangeHandler);
|
|
233
|
+
// Compute defaults
|
|
234
|
+
const formDefaults = buildFormDefaults(formElements, props.defaults);
|
|
235
|
+
// Assign defaults to state
|
|
236
|
+
this.state.formData = formDefaults;
|
|
237
|
+
|
|
238
|
+
// Handle fixed visibilities
|
|
239
|
+
formElements.forEach(element => {
|
|
240
|
+
// Assume as visible except it's explicitly set to "false"
|
|
241
|
+
this.state.elementVisibility[element.id] = element.visible !== false;
|
|
242
|
+
});
|
|
243
|
+
this.actionListener = new ActionListener(buildProvinceList, formDefaults);
|
|
244
|
+
this.actionListener.attachAll(formElements);
|
|
245
|
+
|
|
246
|
+
// Sort the elements after attaching action listeners to keep action hierarchy same as creation
|
|
247
|
+
this.formElements = formElements.sort(this.elementSortFunc);
|
|
248
|
+
|
|
249
|
+
// Assemble combined country/province list based on the config element
|
|
250
|
+
const _countryElement = this.formElements.find(el => el.type === ELEMENT_TYPE_COUNTRY);
|
|
251
|
+
if (_countryElement) {
|
|
252
|
+
this.countryList = buildCountryList(_countryElement, emptySelectOption);
|
|
253
|
+
const provinceElement = this.formElements.find(el => el.type === ELEMENT_TYPE_PROVINCE);
|
|
254
|
+
if (provinceElement && provinceElement.required && !!formDefaults[_countryElement.id] && !formDefaults[provinceElement.id]) {
|
|
255
|
+
// Set default for province field for given country
|
|
256
|
+
const [first] = Object.values(buildProvinceList(formDefaults[_countryElement.id]));
|
|
257
|
+
if (first) {
|
|
258
|
+
this.state.formData[provinceElement.id] = first;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Final form initialization, by triggering actionListeners and enable rendering for elements
|
|
264
|
+
let _newState = this.state;
|
|
265
|
+
this.formElements.forEach(element => {
|
|
266
|
+
_newState = this.actionListener.notify(element.id, this.state, _newState);
|
|
267
|
+
});
|
|
268
|
+
this.state = _newState;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
56
272
|
* Handles response of validation errors
|
|
57
273
|
* @param {Object} nextProps The new props object with changed data
|
|
58
|
-
*/
|
|
274
|
+
*/
|
|
275
|
+
UNSAFE_componentWillReceiveProps(nextProps) {
|
|
276
|
+
const oldValidationErrors = buildValidationErrorList(this.props.validationErrors);
|
|
277
|
+
const newValidationErrors = buildValidationErrorList(nextProps.validationErrors);
|
|
278
|
+
if (!_isEqual(oldValidationErrors, newValidationErrors)) {
|
|
279
|
+
this.setState({
|
|
280
|
+
errors: newValidationErrors
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
59
285
|
* Renders the component based on the given config
|
|
60
286
|
* @return {JSX}
|
|
61
|
-
*/
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
287
|
+
*/
|
|
288
|
+
render() {
|
|
289
|
+
return /*#__PURE__*/React.createElement(Fragment, null, /*#__PURE__*/React.createElement(Form, {
|
|
290
|
+
onSubmit: this.props.onSubmit
|
|
291
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
292
|
+
className: this.props.className
|
|
293
|
+
}, this.formElements.map(element => /*#__PURE__*/React.createElement(Fragment, {
|
|
294
|
+
key: `${this.props.name}.${element.id}`
|
|
295
|
+
}, /*#__PURE__*/React.createElement(Portal, {
|
|
296
|
+
name: `${sanitize(this.props.name)}.${sanitize(element.id)}.${BEFORE}`
|
|
297
|
+
}), /*#__PURE__*/React.createElement(Portal, {
|
|
298
|
+
name: `${sanitize(this.props.name)}.${sanitize(element.id)}`
|
|
299
|
+
}, this.renderElement(element)), /*#__PURE__*/React.createElement(Portal, {
|
|
300
|
+
name: `${sanitize(this.props.name)}.${sanitize(element.id)}.${AFTER}`
|
|
301
|
+
}))))));
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
Builder.defaultProps = {
|
|
305
|
+
className: '',
|
|
306
|
+
defaults: {},
|
|
307
|
+
onSubmit: () => {},
|
|
308
|
+
validationErrors: []
|
|
309
|
+
};
|
|
310
|
+
export default Builder;
|