@shopgate/pwa-common 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/App.js +48 -6
- package/action-creators/app/index.js +75 -12
- package/action-creators/app/spec.js +96 -1
- package/action-creators/client/index.js +27 -5
- package/action-creators/client/spec.js +44 -1
- package/action-creators/error/index.js +15 -3
- package/action-creators/index.js +9 -1
- package/action-creators/menu/index.js +23 -4
- package/action-creators/menu/spec.js +37 -1
- package/action-creators/modal/index.js +15 -3
- package/action-creators/modal/spec.js +26 -1
- package/action-creators/page/index.js +24 -4
- package/action-creators/page/spec.js +38 -1
- package/action-creators/router/index.js +48 -7
- package/action-creators/url/index.js +24 -4
- package/action-creators/url/spec.js +45 -1
- package/action-creators/user/index.js +90 -13
- package/action-creators/user/spec.js +186 -2
- package/actions/app/handleDeepLink.js +11 -2
- package/actions/app/handleLink.js +62 -6
- package/actions/app/handlePushNotification.js +32 -4
- package/actions/app/handleUniversalLink.js +11 -2
- package/actions/app/registerLinkEvents.js +24 -3
- package/actions/client/fetchClientInformation.js +26 -2
- package/actions/menu/fetchMenu.js +23 -2
- package/actions/modal/closeModal.js +18 -2
- package/actions/modal/promiseMap.js +3 -1
- package/actions/modal/showModal.js +54 -8
- package/actions/page/fetchPageConfig.js +69 -2
- package/actions/page/getPageConfig.js +6 -2
- package/actions/page/index.js +1 -1
- package/actions/router/historyPop.js +12 -2
- package/actions/router/historyPopToRoute.js +27 -2
- package/actions/router/historyPush.js +12 -2
- package/actions/router/historyRedirect.js +21 -2
- package/actions/router/historyReplace.js +20 -3
- package/actions/router/historyReset.js +11 -2
- package/actions/router/historyResetTo.js +12 -2
- package/actions/router/index.js +17 -1
- package/actions/router/routeDidPop.js +11 -2
- package/actions/router/routeDidPush.js +13 -2
- package/actions/router/routeDidReplace.js +11 -2
- package/actions/router/routeDidReset.js +11 -2
- package/actions/router/routeDidUpdate.js +10 -2
- package/actions/router/routeWillPop.js +11 -2
- package/actions/router/routeWillPush.js +13 -2
- package/actions/router/routeWillReplace.js +11 -2
- package/actions/router/routeWillReset.js +11 -2
- package/actions/router/windowOpenOverride.js +10 -2
- package/actions/user/fetchRegisterUrl.js +36 -2
- package/actions/user/fetchUser.js +29 -3
- package/actions/user/getUser.js +6 -2
- package/actions/user/index.js +1 -1
- package/actions/user/login.js +76 -9
- package/actions/user/logout.js +30 -2
- package/collections/AuthRoutes.js +73 -14
- package/collections/Configuration.js +54 -7
- package/collections/EmbeddedMedia.js +84 -11
- package/collections/PersistedReducers.js +41 -6
- package/collections/Redirects.js +103 -17
- package/collections/index.js +5 -1
- package/collections/media-providers/MediaProvider.js +151 -26
- package/collections/media-providers/Vimeo.js +113 -19
- package/collections/media-providers/YouTube.js +74 -14
- package/collections/media-providers/index.js +3 -1
- package/collections/media-providers/style.js +52 -2
- package/components/Backdrop/index.js +95 -6
- package/components/Backdrop/spec.js +23 -1
- package/components/Backdrop/style.js +11 -2
- package/components/Button/index.js +47 -5
- package/components/Button/spec.js +36 -1
- package/components/Button/style.js +6 -1
- package/components/Checkbox/index.js +126 -32
- package/components/Checkbox/spec.js +94 -3
- package/components/Consume/helpers/buildParams.js +13 -2
- package/components/Consume/index.js +14 -2
- package/components/CountdownTimer/index.js +115 -17
- package/components/CountdownTimer/spec.js +126 -12
- package/components/Drawer/index.js +131 -16
- package/components/Drawer/spec.js +76 -1
- package/components/Drawer/style.js +37 -1
- package/components/Dropdown/index.js +65 -6
- package/components/Dropdown/style.js +4 -1
- package/components/Dropdown/transitions.js +34 -1
- package/components/Ellipsis/index.js +16 -2
- package/components/Ellipsis/spec.js +13 -1
- package/components/EmbeddedMedia/index.js +56 -6
- package/components/EmbeddedMedia/spec.js +52 -3
- package/components/ErrorBoundary/connector.js +9 -2
- package/components/ErrorBoundary/index.js +43 -7
- package/components/Grid/components/Item/index.js +40 -4
- package/components/Grid/components/Item/spec.js +23 -1
- package/components/Grid/components/Item/style.js +17 -3
- package/components/Grid/index.js +36 -4
- package/components/Grid/spec.js +23 -1
- package/components/Grid/style.js +11 -2
- package/components/HtmlSanitizer/connector.js +24 -3
- package/components/HtmlSanitizer/index.js +104 -12
- package/components/HtmlSanitizer/spec.js +207 -6
- package/components/I18n/components/FormatDate/index.js +26 -2
- package/components/I18n/components/FormatDate/spec.js +46 -1
- package/components/I18n/components/FormatNumber/index.js +34 -2
- package/components/I18n/components/FormatNumber/spec.js +41 -2
- package/components/I18n/components/FormatPrice/index.js +32 -2
- package/components/I18n/components/FormatPrice/spec.js +46 -1
- package/components/I18n/components/FormatTime/index.js +26 -2
- package/components/I18n/components/FormatTime/spec.js +43 -2
- package/components/I18n/components/I18nProvider/index.js +52 -9
- package/components/I18n/components/I18nProvider/spec.js +39 -1
- package/components/I18n/components/Placeholder/index.js +8 -2
- package/components/I18n/components/Placeholder/spec.js +30 -1
- package/components/I18n/components/Translate/index.js +68 -7
- package/components/I18n/components/Translate/spec.js +30 -1
- package/components/I18n/index.js +16 -1
- package/components/Icon/index.js +25 -2
- package/components/Icon/style.js +6 -1
- package/components/Image/Image.js +176 -19
- package/components/Image/ImageInner.js +48 -2
- package/components/Image/index.js +1 -1
- package/components/Image/style.js +29 -2
- package/components/InfiniteContainer/index.js +381 -49
- package/components/InfiniteContainer/spec.js +199 -10
- package/components/Input/components/DateInput.js +262 -6
- package/components/Input/components/MultiLineInput.js +98 -12
- package/components/Input/components/SimpleInput.js +207 -31
- package/components/Input/index.js +32 -3
- package/components/Input/spec.js +122 -1
- package/components/KeyboardConsumer/index.js +48 -7
- package/components/Link/connector.js +7 -1
- package/components/Link/index.js +96 -11
- package/components/Link/spec.js +56 -1
- package/components/Link/style.js +10 -1
- package/components/List/components/Item/index.js +35 -3
- package/components/List/components/Item/style.js +16 -1
- package/components/List/index.js +20 -2
- package/components/List/spec.js +31 -1
- package/components/Loading/index.js +6 -2
- package/components/Modal/index.js +38 -3
- package/components/Modal/style.js +36 -1
- package/components/ModalContainer/connector.js +17 -3
- package/components/ModalContainer/index.js +36 -3
- package/components/ModalContainer/spec.js +105 -5
- package/components/Picker/components/Button/index.js +34 -2
- package/components/Picker/components/Button/style.js +19 -1
- package/components/Picker/components/List/index.js +33 -2
- package/components/Picker/components/List/style.js +17 -1
- package/components/Picker/components/Modal/index.js +60 -7
- package/components/Picker/components/Modal/style.js +78 -1
- package/components/Picker/index.js +167 -21
- package/components/Picker/spec.js +83 -2
- package/components/Portal/index.js +130 -19
- package/components/ProductCharacteristics/connector.js +33 -4
- package/components/ProductCharacteristics/context.js +2 -1
- package/components/ProductCharacteristics/helpers/index.js +135 -21
- package/components/ProductCharacteristics/index.js +266 -31
- package/components/RangeSlider/components/Handle/index.js +25 -2
- package/components/RangeSlider/components/Handle/style.js +14 -1
- package/components/RangeSlider/helper.js +43 -8
- package/components/RangeSlider/index.js +228 -38
- package/components/RangeSlider/style.js +14 -1
- package/components/Route/RouteNotFound.js +46 -3
- package/components/Route/index.js +78 -10
- package/components/Router/connector.js +9 -2
- package/components/Router/index.js +237 -31
- package/components/ScannerContainer/connector.js +9 -2
- package/components/ScannerContainer/index.js +42 -6
- package/components/Select/components/Item/index.js +20 -4
- package/components/Select/components/Item/style.js +4 -1
- package/components/Select/index.js +149 -28
- package/components/Select/spec.js +86 -2
- package/components/Select/style.js +17 -1
- package/components/SelectBox/components/Item/index.js +47 -5
- package/components/SelectBox/components/Item/style.js +7 -1
- package/components/SelectBox/index.js +173 -17
- package/components/SelectBox/spec.js +59 -3
- package/components/SelectBox/style.js +18 -1
- package/components/Slider/index.js +6 -2
- package/components/SurroundPortals/index.js +26 -2
- package/components/Swiper/components/SwiperItem/index.js +28 -4
- package/components/Swiper/components/SwiperItem/spec.js +17 -1
- package/components/Swiper/components/SwiperItem/styles.js +5 -1
- package/components/Swiper/index.js +210 -18
- package/components/Swiper/styles.js +75 -7
- package/components/Toaster/index.js +10 -2
- package/components/Transition/index.js +89 -13
- package/components/Widgets/components/Widget/index.js +52 -4
- package/components/Widgets/components/Widget/spec.js +68 -3
- package/components/Widgets/components/Widget/style.js +21 -3
- package/components/Widgets/components/WidgetGrid/index.js +52 -7
- package/components/Widgets/components/WidgetGrid/spec.js +46 -2
- package/components/Widgets/components/WidgetGrid/style.js +8 -1
- package/components/Widgets/helpers/shouldShowWidget.js +44 -7
- package/components/Widgets/index.js +127 -15
- package/components/Widgets/spec.js +213 -6
- package/components/index.js +9 -1
- package/constants/ActionTypes.js +97 -19
- package/constants/Configuration.js +12 -2
- package/constants/Device.js +29 -2
- package/constants/DisplayOptions.js +8 -1
- package/constants/MenuIDs.js +2 -1
- package/constants/ModalTypes.js +1 -1
- package/constants/PageIDs.js +1 -1
- package/constants/Pipelines.js +7 -1
- package/constants/Portals.js +136 -3
- package/constants/Registration.js +3 -1
- package/constants/RoutePaths.js +13 -2
- package/constants/Tracking.js +3 -1
- package/constants/client.js +6 -1
- package/constants/ui.js +2 -1
- package/constants/user.js +6 -2
- package/context/index.js +33 -3
- package/helpers/config/index.js +139 -21
- package/helpers/config/mock.js +200 -8
- package/helpers/config/theme.js +50 -4
- package/helpers/data/index.js +204 -29
- package/helpers/data/spec.js +187 -7
- package/helpers/date/index.js +58 -6
- package/helpers/date/spec.js +92 -1
- package/helpers/dom/index.js +48 -11
- package/helpers/environment/index.js +14 -2
- package/helpers/html/decodeHTML.js +7 -1
- package/helpers/html/handleDOM.js +172 -21
- package/helpers/html/parseHTML.js +67 -12
- package/helpers/i18n/getDateFormatter.js +23 -4
- package/helpers/i18n/getNumberFormatter.js +32 -4
- package/helpers/i18n/getPriceFormatter.js +38 -4
- package/helpers/i18n/getTimeFormatter.js +23 -4
- package/helpers/i18n/getTranslator.js +62 -8
- package/helpers/i18n/index.js +5 -1
- package/helpers/i18n/mergeTranslations.js +36 -9
- package/helpers/i18n/messageCache.js +3 -1
- package/helpers/legacy/index.js +47 -9
- package/helpers/modal/withShowModal.js +13 -2
- package/helpers/portals/portalCollection.js +28 -6
- package/helpers/portals/routePortals.js +12 -1
- package/helpers/redux/compareObjects.js +7 -2
- package/helpers/redux/generateResultHash.js +36 -3
- package/helpers/redux/generateSortedHash.js +7 -2
- package/helpers/redux/hasExpired.js +10 -2
- package/helpers/redux/index.js +7 -1
- package/helpers/redux/mutable.js +143 -24
- package/helpers/redux/shouldFetchData.js +46 -10
- package/helpers/redux/shouldFetchFilters.js +17 -4
- package/helpers/router/index.js +49 -5
- package/helpers/style/index.js +43 -4
- package/helpers/style/spec.js +108 -2
- package/helpers/tracking/index.js +52 -9
- package/helpers/validation/index.js +39 -12
- package/helpers/validation/spec.js +10 -1
- package/package.json +3 -3
- package/providers/index.js +4 -1
- package/providers/loading/context.js +2 -1
- package/providers/loading/index.js +137 -22
- package/providers/toast/context.js +2 -1
- package/providers/toast/index.js +105 -11
- package/reducers/client/connectivity.js +22 -2
- package/reducers/client/index.js +7 -1
- package/reducers/client/info.js +27 -2
- package/reducers/index.js +23 -4
- package/reducers/menu/index.js +5 -1
- package/reducers/menu/menusById.js +41 -2
- package/reducers/modal/index.js +14 -2
- package/reducers/page/index.js +68 -5
- package/reducers/router/index.js +48 -2
- package/reducers/url/index.js +42 -3
- package/reducers/user/data.js +27 -2
- package/reducers/user/index.js +7 -1
- package/reducers/user/login.js +65 -2
- package/selectors/client.js +138 -21
- package/selectors/history.js +49 -11
- package/selectors/menu.js +34 -6
- package/selectors/modal.js +15 -4
- package/selectors/page.js +25 -4
- package/selectors/router.js +154 -30
- package/selectors/url.js +25 -4
- package/selectors/user.js +90 -13
- package/store/index.js +60 -6
- package/store/middelwares/logger.js +7 -1
- package/store/middelwares/streams.js +19 -2
- package/streams/app.js +60 -8
- package/streams/client.js +8 -2
- package/streams/error.js +14 -3
- package/streams/index.js +6 -1
- package/streams/interval.js +6 -2
- package/streams/main.js +27 -2
- package/streams/router.js +45 -8
- package/streams/user.js +89 -15
- package/streams/view.js +97 -25
- package/styles/reset/form.js +57 -5
- package/styles/reset/index.js +6 -1
- package/styles/reset/media.js +22 -1
- package/styles/reset/root.js +33 -1
- package/styles/reset/table.js +10 -1
- package/styles/reset/typography.js +26 -1
- package/subscriptions/app.js +148 -17
- package/subscriptions/error.js +292 -13
- package/subscriptions/helpers/buildRegisterUrl.js +25 -6
- package/subscriptions/helpers/clearUpInAppBrowser.js +14 -3
- package/subscriptions/helpers/handleLinks.js +267 -25
- package/subscriptions/helpers/pipeline.js +12 -1
- package/subscriptions/history.js +34 -6
- package/subscriptions/index.js +25 -4
- package/subscriptions/menu.js +22 -5
- package/subscriptions/mock.js +39 -7
- package/subscriptions/router.js +336 -23
- package/subscriptions/user.js +93 -3
|
@@ -1,12 +1,41 @@
|
|
|
1
|
-
import{connect}from'react-redux';
|
|
1
|
+
import { connect } from 'react-redux';
|
|
2
|
+
import { router } from '@shopgate/pwa-common/helpers/router';
|
|
3
|
+
import isEqual from 'lodash/isEqual';
|
|
4
|
+
import { getProductVariants } from '@shopgate/pwa-common-commerce/product/selectors/product';
|
|
5
|
+
|
|
6
|
+
/**
|
|
2
7
|
* Maps the contents of the state to the component props.
|
|
3
8
|
* @param {Object} state The current application state.
|
|
4
9
|
* @param {Object} props The component props.
|
|
5
10
|
* @return {Object} The extended component props.
|
|
6
|
-
*/
|
|
11
|
+
*/
|
|
12
|
+
const mapStateToProps = (state, props) => ({
|
|
13
|
+
variants: getProductVariants(state, props)
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
/**
|
|
7
17
|
* @return {Object}
|
|
8
|
-
*/
|
|
18
|
+
*/
|
|
19
|
+
const mapDispatchToProps = () => ({
|
|
20
|
+
navigate: productId => {
|
|
21
|
+
const route = router.getCurrentRoute();
|
|
22
|
+
router.update(route.id, {
|
|
23
|
+
productId
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
/**
|
|
9
29
|
* @param {Object} next The next component props.
|
|
10
30
|
* @param {Object} prev The previous component props.
|
|
11
31
|
* @returns {boolean}
|
|
12
|
-
*/
|
|
32
|
+
*/
|
|
33
|
+
const areStatePropsEqual = (next, prev) => {
|
|
34
|
+
if (!prev.variants && next.variants || !isEqual(prev.variants, next.variants)) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
return true;
|
|
38
|
+
};
|
|
39
|
+
export default connect(mapStateToProps, mapDispatchToProps, null, {
|
|
40
|
+
areStatePropsEqual
|
|
41
|
+
});
|
|
@@ -1 +1,2 @@
|
|
|
1
|
-
import React from'react';
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export default /*#__PURE__*/React.createContext();
|
|
@@ -1,19 +1,48 @@
|
|
|
1
|
-
|
|
1
|
+
import "core-js/modules/es.array.reduce.js";
|
|
2
|
+
import isMatch from 'lodash/isMatch';
|
|
3
|
+
import appConfig from '@shopgate/pwa-common/helpers/config';
|
|
4
|
+
const {
|
|
5
|
+
variantSelectionMode,
|
|
6
|
+
product: {
|
|
7
|
+
variantPreselect
|
|
8
|
+
} = {}
|
|
9
|
+
} = appConfig;
|
|
10
|
+
const preselectVariant = variantPreselect || parseInt(variantSelectionMode, 10) === 1;
|
|
11
|
+
|
|
12
|
+
/**
|
|
2
13
|
* Returns the index of a particular characteristic from a set of characteristics.
|
|
3
14
|
* @param {Array} characteristics The characteristics of a product.
|
|
4
15
|
* @param {number} characteristicId The id of the characteristic to find.
|
|
5
16
|
* @return {number}
|
|
6
|
-
*/
|
|
17
|
+
*/
|
|
18
|
+
export function findSelectionIndex(characteristics, characteristicId) {
|
|
19
|
+
return characteristics.findIndex(char => char.id === characteristicId);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
7
23
|
* Returns true if the characteristic at the given index is the next characteristic to be selected.
|
|
8
24
|
* @param {Array} characteristics The characteristics of a product.
|
|
9
25
|
* @param {number} index The index of the characteristic to check.
|
|
10
26
|
* @return {boolean}
|
|
11
|
-
*/
|
|
27
|
+
*/
|
|
28
|
+
export function isCharacteristicEnabled(characteristics, index) {
|
|
29
|
+
if (index === 0) {
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
return !!Object.values(characteristics)[index - 1];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
12
36
|
* Returns the selected value by characteristic ID.
|
|
13
37
|
* @param {string} charId The characteristic ID.
|
|
14
38
|
* @param {Object} characteristics The already selected characteristics.
|
|
15
39
|
* @return {string|null}
|
|
16
|
-
*/
|
|
40
|
+
*/
|
|
41
|
+
export function getSelectedValue(charId, characteristics) {
|
|
42
|
+
return characteristics[charId] ? characteristics[charId] : null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
17
46
|
* Prepares the new state after a selection has been made.
|
|
18
47
|
* @param {string} id The selection ID
|
|
19
48
|
* @param {string} value The selection value.
|
|
@@ -21,28 +50,113 @@ function _defineProperty(obj,key,value){if(key in obj){Object.defineProperty(obj
|
|
|
21
50
|
* @param {Array} characteristics The characteristics of a product.
|
|
22
51
|
* @param {Array} products All available products.
|
|
23
52
|
* @return {Object}
|
|
24
|
-
*/
|
|
25
|
-
|
|
26
|
-
|
|
53
|
+
*/
|
|
54
|
+
export function prepareState(id, value, selections, characteristics, products) {
|
|
55
|
+
const updateValid = !!characteristics.find(({
|
|
56
|
+
id: cId,
|
|
57
|
+
values
|
|
58
|
+
}) => {
|
|
59
|
+
if (cId !== id) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
return !!values.find(({
|
|
63
|
+
id: vId
|
|
64
|
+
}) => vId === value);
|
|
65
|
+
});
|
|
66
|
+
if (!updateValid) {
|
|
67
|
+
// Input parameters are invalid.
|
|
68
|
+
return selections;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Merge the just changed value into the previous selection.
|
|
72
|
+
const currentSelection = {
|
|
73
|
+
...selections,
|
|
74
|
+
[id]: value
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
/**
|
|
27
78
|
* Determine if there are products for the current user selection.
|
|
28
|
-
*/
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
79
|
+
*/
|
|
80
|
+
const matches = products.filter(product => isMatch(product.characteristics, currentSelection));
|
|
81
|
+
if (matches.length === 1) {
|
|
82
|
+
// Product found for the current selection.
|
|
83
|
+
return {
|
|
84
|
+
...matches[0].characteristics
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
if (!selections[id]) {
|
|
88
|
+
// Changed value wasn't set before.
|
|
89
|
+
return currentSelection;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Find the current index.
|
|
93
|
+
const index = findSelectionIndex(characteristics, id);
|
|
94
|
+
|
|
95
|
+
/**
|
|
32
96
|
* When the given index is the same as the complete set
|
|
33
97
|
* of characteristics then there is nothing to reset.
|
|
34
|
-
*/
|
|
35
|
-
|
|
36
|
-
|
|
98
|
+
*/
|
|
99
|
+
if (index === characteristics.length - 1) {
|
|
100
|
+
return currentSelection;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Find the selections after the given index.
|
|
104
|
+
const after = characteristics.slice(index + 1);
|
|
105
|
+
|
|
106
|
+
// Delete the found selections.
|
|
107
|
+
after.forEach(item => {
|
|
108
|
+
delete currentSelection[item.id];
|
|
109
|
+
});
|
|
110
|
+
return currentSelection;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
37
113
|
* Preselect characteristics for variant
|
|
38
114
|
* or pre-select the first available product
|
|
39
115
|
* @param {string} [variantId=null] The selected variant
|
|
40
116
|
* @param {{products: Object[], characteristics: Object[]}} [variants=null] All possible variants.
|
|
41
117
|
* @return {Object}
|
|
42
|
-
*/
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
return
|
|
118
|
+
*/
|
|
119
|
+
export function selectCharacteristics({
|
|
120
|
+
variantId,
|
|
121
|
+
variants = {}
|
|
122
|
+
}) {
|
|
123
|
+
if (!variants || !variants.products || !variants.products.length) {
|
|
124
|
+
return {};
|
|
125
|
+
}
|
|
126
|
+
if (variantId) {
|
|
127
|
+
const variant = variants.products.find(product => product.id === variantId) || {};
|
|
128
|
+
return {
|
|
129
|
+
...variant.characteristics
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
// If product has only 1 variant preselect no matter if "preselect" is chosen or not.
|
|
133
|
+
if (variants.products.length === 1) {
|
|
134
|
+
return {
|
|
135
|
+
...variants.products[0].characteristics
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
// Pre-selection is off
|
|
139
|
+
if (!preselectVariant) {
|
|
140
|
+
return {};
|
|
141
|
+
}
|
|
142
|
+
// Find the first selectable product by characteristics
|
|
143
|
+
return variants.characteristics.reduce((acc, char) => {
|
|
144
|
+
// Find the first char value with selectable products
|
|
145
|
+
const firstVal = char.values.find(val => {
|
|
146
|
+
// eslint-disable-next-line extra-rules/no-single-line-objects
|
|
147
|
+
const source = {
|
|
148
|
+
...acc,
|
|
149
|
+
[char.id]: val.id
|
|
150
|
+
};
|
|
151
|
+
return variants.products.filter(p => isMatch(p.characteristics, source)).length > 0;
|
|
152
|
+
});
|
|
153
|
+
if (!firstVal) {
|
|
154
|
+
return acc;
|
|
155
|
+
}
|
|
156
|
+
// eslint-disable-next-line extra-rules/no-single-line-objects
|
|
157
|
+
return {
|
|
158
|
+
...acc,
|
|
159
|
+
[char.id]: firstVal.id
|
|
160
|
+
};
|
|
161
|
+
}, {});
|
|
162
|
+
}
|
|
@@ -1,38 +1,273 @@
|
|
|
1
|
-
|
|
1
|
+
import React, { Component } from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import isMatch from 'lodash/isMatch';
|
|
4
|
+
import { broadcastLiveMessage } from '@shopgate/engage/a11y';
|
|
5
|
+
import { responsiveCondition } from '@shopgate/engage/styles';
|
|
6
|
+
import connect from "./connector";
|
|
7
|
+
import VariantsContext from "./context";
|
|
8
|
+
import { isCharacteristicEnabled, getSelectedValue, prepareState, selectCharacteristics } from "./helpers";
|
|
9
|
+
|
|
10
|
+
/**
|
|
2
11
|
* The ProductCharacteristics component.
|
|
3
12
|
* @deprecated Please use the component via
|
|
4
13
|
* `import { ProductCharacteristics } from '@shopgate/engage/product'`
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
14
|
+
*/
|
|
15
|
+
class ProductCharacteristics extends Component {
|
|
16
|
+
/**
|
|
17
|
+
* @param {Object} props The component props.
|
|
18
|
+
*/
|
|
19
|
+
constructor(_props) {
|
|
20
|
+
super(_props);
|
|
21
|
+
/**
|
|
22
|
+
* Sets the refs to the characteristics selects.
|
|
23
|
+
* @param {Object} props The props to check against.
|
|
24
|
+
*/
|
|
25
|
+
this.setRefs = props => {
|
|
26
|
+
const {
|
|
27
|
+
variants
|
|
28
|
+
} = props;
|
|
29
|
+
if (variants) {
|
|
30
|
+
variants.characteristics.forEach(char => {
|
|
31
|
+
this.refsStore[char.id] = /*#__PURE__*/React.createRef();
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Checks if all selections have been made.
|
|
37
|
+
* @return {boolean}
|
|
38
|
+
*/
|
|
39
|
+
this.checkSelection = () => {
|
|
40
|
+
const {
|
|
41
|
+
characteristics
|
|
42
|
+
} = this.state;
|
|
43
|
+
const {
|
|
44
|
+
variants,
|
|
45
|
+
variantId
|
|
46
|
+
} = this.props;
|
|
47
|
+
if (!variants) {
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
const filteredValues = Object.keys(characteristics).filter(key => !!characteristics[key]);
|
|
51
|
+
const selected = !!(filteredValues.length === variants.characteristics.length && variantId);
|
|
52
|
+
if (!selected) {
|
|
53
|
+
const firstUnselected = this.findUnselectedCharacteristic();
|
|
54
|
+
if (firstUnselected) {
|
|
55
|
+
const ref = this.refsStore[firstUnselected.id];
|
|
56
|
+
|
|
57
|
+
// Focus the item for screen readers and broadcast a related live message.
|
|
58
|
+
ref.current.focus();
|
|
59
|
+
const option = ref.current.innerText;
|
|
60
|
+
broadcastLiveMessage('product.pick_option_first', {
|
|
61
|
+
params: {
|
|
62
|
+
option
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
if (responsiveCondition('>xs', {
|
|
66
|
+
webOnly: true
|
|
67
|
+
})) {
|
|
68
|
+
ref.current.scrollIntoView({
|
|
69
|
+
behavior: 'smooth',
|
|
70
|
+
block: 'center'
|
|
71
|
+
});
|
|
72
|
+
} else {
|
|
73
|
+
ref.current.scrollIntoView({
|
|
74
|
+
behavior: 'smooth'
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
this.setState({
|
|
78
|
+
highlight: firstUnselected.id
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return selected;
|
|
83
|
+
};
|
|
84
|
+
this.checkSelectedCharacteristics = () => {
|
|
85
|
+
const {
|
|
86
|
+
characteristics
|
|
87
|
+
} = this.state;
|
|
88
|
+
const {
|
|
89
|
+
variantId,
|
|
90
|
+
variants,
|
|
91
|
+
finishTimeout
|
|
92
|
+
} = this.props;
|
|
93
|
+
if (!variants) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const filteredValues = Object.keys(characteristics).filter(key => !!characteristics[key]);
|
|
97
|
+
if (filteredValues.length !== variants.characteristics.length) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const products = variants.products.filter(product => isMatch(product.characteristics, characteristics));
|
|
101
|
+
if (!products.length) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
if (products[0].id === variantId) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
setTimeout(() => {
|
|
108
|
+
this.props.navigate(products[0].id);
|
|
109
|
+
}, finishTimeout);
|
|
110
|
+
};
|
|
111
|
+
/**
|
|
112
|
+
* Stores a selected characteristic into the local state.
|
|
113
|
+
* @param {Object} selection The selected item.
|
|
114
|
+
*/
|
|
115
|
+
this.handleSelection = selection => {
|
|
116
|
+
const {
|
|
117
|
+
variants,
|
|
118
|
+
setCharacteristics
|
|
119
|
+
} = this.props;
|
|
120
|
+
const {
|
|
121
|
+
id,
|
|
122
|
+
value
|
|
123
|
+
} = selection;
|
|
124
|
+
this.setState(({
|
|
125
|
+
characteristics
|
|
126
|
+
}) => {
|
|
127
|
+
const state = prepareState(id, value, characteristics, variants.characteristics, variants.products);
|
|
128
|
+
setCharacteristics(state);
|
|
129
|
+
return {
|
|
130
|
+
characteristics: {
|
|
131
|
+
...state
|
|
132
|
+
},
|
|
133
|
+
highlight: null
|
|
134
|
+
};
|
|
135
|
+
}, this.checkSelectedCharacteristics);
|
|
136
|
+
};
|
|
137
|
+
/**
|
|
138
|
+
* @param {Object} selections The selections stored in the state.
|
|
139
|
+
* @param {string} charId The current characteristic ID.
|
|
140
|
+
* @param {Array} values The characteristic values.
|
|
141
|
+
* @param {number} charIndex The characteristic index.
|
|
142
|
+
* @param {string|null} selectedValue selectedValue
|
|
143
|
+
* @return {Array}
|
|
144
|
+
*/
|
|
145
|
+
this.buildValues = (selections, charId, values, charIndex, selectedValue) => {
|
|
146
|
+
// If this is the first characteristic then all values are selectable.
|
|
147
|
+
if (charIndex === 0) {
|
|
148
|
+
return values.map(value => ({
|
|
149
|
+
...value,
|
|
150
|
+
selectable: true,
|
|
151
|
+
selected: selectedValue === value.id
|
|
152
|
+
}));
|
|
153
|
+
}
|
|
154
|
+
const {
|
|
155
|
+
variants
|
|
156
|
+
} = this.props;
|
|
157
|
+
const subset = {};
|
|
158
|
+
Object.keys(selections).forEach((item, index) => {
|
|
159
|
+
if (index < charIndex) {
|
|
160
|
+
subset[item] = selections[item];
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
// Filter products that match or partially match the current characteristic selection.
|
|
165
|
+
const products = variants.products.filter(({
|
|
166
|
+
characteristics
|
|
167
|
+
}) => isMatch(characteristics, subset));
|
|
168
|
+
|
|
169
|
+
// Check if any of the values are present inside any of the matching products.
|
|
170
|
+
return values.map(value => {
|
|
171
|
+
const selectable = products.some(({
|
|
172
|
+
characteristics
|
|
173
|
+
}) => isMatch(characteristics, {
|
|
174
|
+
[charId]: value.id
|
|
175
|
+
}));
|
|
176
|
+
return {
|
|
177
|
+
...value,
|
|
178
|
+
selectable,
|
|
179
|
+
selected: selectedValue === value.id
|
|
180
|
+
};
|
|
181
|
+
});
|
|
182
|
+
};
|
|
183
|
+
/**
|
|
184
|
+
* Resets the highlight state
|
|
185
|
+
*/
|
|
186
|
+
this.resetHighlight = () => {
|
|
187
|
+
this.setState({
|
|
188
|
+
highlight: null
|
|
189
|
+
});
|
|
190
|
+
};
|
|
191
|
+
this.refsStore = {};
|
|
192
|
+
this.state = {
|
|
193
|
+
highlight: null,
|
|
194
|
+
characteristics: selectCharacteristics(_props)
|
|
195
|
+
};
|
|
196
|
+
this.setRefs(_props);
|
|
197
|
+
_props.conditioner.addConditioner('product-variants', this.checkSelection);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/** @inheritDoc */
|
|
201
|
+
componentDidMount() {
|
|
202
|
+
this.checkSelectedCharacteristics();
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
28
206
|
* @param {Object} nextProps The next component props.
|
|
29
|
-
*/
|
|
30
|
-
|
|
207
|
+
*/
|
|
208
|
+
UNSAFE_componentWillReceiveProps(nextProps) {
|
|
209
|
+
if (!this.props.variants && nextProps.variants) {
|
|
210
|
+
// Initialize refs and characteristics when the variants prop was updated with a valid value.
|
|
211
|
+
this.setRefs(nextProps);
|
|
212
|
+
this.setState({
|
|
213
|
+
characteristics: selectCharacteristics(nextProps)
|
|
214
|
+
}, this.checkSelectedCharacteristics);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
31
218
|
* Finds the first unselected characteristic.
|
|
32
219
|
* @return {Object|null}
|
|
33
|
-
*/
|
|
220
|
+
*/
|
|
221
|
+
findUnselectedCharacteristic() {
|
|
222
|
+
const {
|
|
223
|
+
characteristics
|
|
224
|
+
} = this.state;
|
|
225
|
+
const unselected = this.props.variants.characteristics.filter(char => !characteristics.hasOwnProperty(char.id));
|
|
226
|
+
if (unselected.length) {
|
|
227
|
+
return unselected[0];
|
|
228
|
+
}
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
34
233
|
* @return {JSX}
|
|
35
|
-
*/
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
234
|
+
*/
|
|
235
|
+
render() {
|
|
236
|
+
const {
|
|
237
|
+
characteristics
|
|
238
|
+
} = this.state;
|
|
239
|
+
const {
|
|
240
|
+
variants
|
|
241
|
+
} = this.props;
|
|
242
|
+
if (!variants) {
|
|
243
|
+
return null;
|
|
244
|
+
}
|
|
245
|
+
return /*#__PURE__*/React.createElement(VariantsContext.Provider, {
|
|
246
|
+
value: this.state
|
|
247
|
+
}, variants.characteristics.map((char, index) => {
|
|
248
|
+
const disabled = !isCharacteristicEnabled(characteristics, index);
|
|
249
|
+
const selected = getSelectedValue(char.id, characteristics);
|
|
250
|
+
const values = this.buildValues(characteristics, char.id, char.values, index, selected);
|
|
251
|
+
return this.props.render({
|
|
252
|
+
charRef: this.refsStore[char.id],
|
|
253
|
+
disabled,
|
|
254
|
+
highlight: this.state.highlight === char.id,
|
|
255
|
+
id: char.id,
|
|
256
|
+
key: char.id,
|
|
257
|
+
label: char.label,
|
|
258
|
+
swatch: !!char.swatch,
|
|
259
|
+
// BETA
|
|
260
|
+
select: this.handleSelection,
|
|
261
|
+
selected,
|
|
262
|
+
values,
|
|
263
|
+
resetHighlight: this.resetHighlight
|
|
264
|
+
});
|
|
265
|
+
}));
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
ProductCharacteristics.defaultProps = {
|
|
269
|
+
finishTimeout: 0,
|
|
270
|
+
variantId: null,
|
|
271
|
+
variants: null
|
|
272
|
+
};
|
|
273
|
+
export default connect(ProductCharacteristics);
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import React from'react';
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import styles from "./style";
|
|
4
|
+
|
|
5
|
+
/**
|
|
2
6
|
* The range slider handle component.
|
|
3
7
|
* @param {Object} props The component properties
|
|
4
8
|
* @param {boolean} props.active Whether this handle is currently touched or was recently touched.
|
|
@@ -6,4 +10,23 @@ import React from'react';import PropTypes from'prop-types';import styles from"./
|
|
|
6
10
|
* @param {Function} props.onTouchStart The touch start event callback.
|
|
7
11
|
* @param {Object} props.classNames (Optional) An additional style classes for the handle.
|
|
8
12
|
* @returns {JSX}
|
|
9
|
-
*/
|
|
13
|
+
*/
|
|
14
|
+
const RangeSliderHandle = ({
|
|
15
|
+
active,
|
|
16
|
+
index,
|
|
17
|
+
onTouchStart,
|
|
18
|
+
classNames
|
|
19
|
+
}) => /*#__PURE__*/React.createElement("div", {
|
|
20
|
+
className: `${classNames.handleOuter || ''} ${styles}`,
|
|
21
|
+
style: {
|
|
22
|
+
zIndex: Number(active) || 0
|
|
23
|
+
},
|
|
24
|
+
onTouchStart: event => onTouchStart(event, index)
|
|
25
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
26
|
+
className: classNames.handleInner || ''
|
|
27
|
+
}));
|
|
28
|
+
RangeSliderHandle.defaultProps = {
|
|
29
|
+
active: false,
|
|
30
|
+
classNames: {}
|
|
31
|
+
};
|
|
32
|
+
export default RangeSliderHandle;
|
|
@@ -1 +1,14 @@
|
|
|
1
|
-
import{css}from'glamor';
|
|
1
|
+
import { css } from 'glamor';
|
|
2
|
+
export default css({
|
|
3
|
+
top: '50%',
|
|
4
|
+
position: 'absolute',
|
|
5
|
+
':first-child': {
|
|
6
|
+
left: 0,
|
|
7
|
+
transform: 'translate(-50%, -50%)'
|
|
8
|
+
},
|
|
9
|
+
':last-child': {
|
|
10
|
+
left: 'auto',
|
|
11
|
+
right: 0,
|
|
12
|
+
transform: 'translate(50%, -50%)'
|
|
13
|
+
}
|
|
14
|
+
});
|