@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,14 +1,67 @@
|
|
|
1
|
-
|
|
1
|
+
import React, { Component } from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import classNames from 'classnames';
|
|
4
|
+
import styles from "./style";
|
|
5
|
+
|
|
6
|
+
/**
|
|
2
7
|
* The picker modal.
|
|
3
|
-
*/
|
|
8
|
+
*/
|
|
9
|
+
class PickerModal extends Component {
|
|
10
|
+
/**
|
|
4
11
|
* The constructor.
|
|
5
12
|
* @param {Object} props The props.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
|
|
13
|
+
*/
|
|
14
|
+
constructor(props) {
|
|
15
|
+
super(props);
|
|
16
|
+
/**
|
|
17
|
+
* Closes the modal after the closing animations have finished.
|
|
18
|
+
*/
|
|
19
|
+
this.closeModal = () => {
|
|
20
|
+
this.setState({
|
|
21
|
+
active: false
|
|
22
|
+
});
|
|
23
|
+
clearTimeout(this.timeout);
|
|
24
|
+
this.timeout = setTimeout(this.props.onClose, styles.duration);
|
|
25
|
+
};
|
|
26
|
+
this.timeout = null;
|
|
27
|
+
this.state = {
|
|
28
|
+
active: true
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
9
33
|
* Update state when isOpen changes.
|
|
10
34
|
* @param {Object} nextProps The next component props.
|
|
11
|
-
*/
|
|
35
|
+
*/
|
|
36
|
+
UNSAFE_componentWillReceiveProps(nextProps) {
|
|
37
|
+
if (this.props.isOpen !== nextProps.isOpen) {
|
|
38
|
+
this.setState({
|
|
39
|
+
active: nextProps.isOpen
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
12
44
|
* Render all the things!
|
|
13
45
|
* @returns {JSX} The picker modal with the picker list inside.
|
|
14
|
-
*/
|
|
46
|
+
*/
|
|
47
|
+
render() {
|
|
48
|
+
const backgroundClassName = classNames(styles.background.base, {
|
|
49
|
+
[styles.background.inactive]: !this.state.active
|
|
50
|
+
});
|
|
51
|
+
const containerClassName = classNames(styles.container.base, {
|
|
52
|
+
[styles.container.inactive]: !this.state.active
|
|
53
|
+
});
|
|
54
|
+
return this.props.isOpen ? /*#__PURE__*/React.createElement("div", {
|
|
55
|
+
className: `${styles.wrapper} common__picker__modal`
|
|
56
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
57
|
+
"aria-hidden": true,
|
|
58
|
+
className: backgroundClassName,
|
|
59
|
+
onClick: this.closeModal
|
|
60
|
+
}), /*#__PURE__*/React.createElement("div", {
|
|
61
|
+
className: containerClassName
|
|
62
|
+
}, /*#__PURE__*/React.cloneElement(this.props.children, {
|
|
63
|
+
onClose: this.closeModal
|
|
64
|
+
}))) : null;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
export default PickerModal;
|
|
@@ -1 +1,78 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { css } from 'glamor';
|
|
2
|
+
import { themeColors } from '@shopgate/pwa-common/helpers/config';
|
|
3
|
+
const fadeDuration = 150;
|
|
4
|
+
const slideDuration = 150;
|
|
5
|
+
const easing = 'cubic-bezier(0.25, 0.1, 0.25, 1)';
|
|
6
|
+
const slideInPickerModal = css.keyframes({
|
|
7
|
+
'0%': {
|
|
8
|
+
transform: 'translateY(100%)'
|
|
9
|
+
},
|
|
10
|
+
'100%': {
|
|
11
|
+
transform: 'translateY(0)'
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
const slideOutPickerModal = css.keyframes({
|
|
15
|
+
'0%': {
|
|
16
|
+
transform: 'translateY(0)'
|
|
17
|
+
},
|
|
18
|
+
'100%': {
|
|
19
|
+
transform: 'translateY(100%)'
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
const fadeInPickerBackground = css.keyframes({
|
|
23
|
+
'0%': {
|
|
24
|
+
opacity: 0
|
|
25
|
+
},
|
|
26
|
+
'100%': {
|
|
27
|
+
opacity: 0.5
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
const fadeOutPickerBackground = css.keyframes({
|
|
31
|
+
'0%': {
|
|
32
|
+
opacity: 0.5
|
|
33
|
+
},
|
|
34
|
+
'100%': {
|
|
35
|
+
opacity: 0
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
const wrapper = css({
|
|
39
|
+
zIndex: 1000,
|
|
40
|
+
position: 'fixed',
|
|
41
|
+
top: 0,
|
|
42
|
+
bottom: 0,
|
|
43
|
+
left: 0,
|
|
44
|
+
right: 0
|
|
45
|
+
}).toString();
|
|
46
|
+
const background = {
|
|
47
|
+
base: css({
|
|
48
|
+
position: 'absolute',
|
|
49
|
+
top: 0,
|
|
50
|
+
bottom: 0,
|
|
51
|
+
left: 0,
|
|
52
|
+
right: 0,
|
|
53
|
+
backgroundColor: 'black',
|
|
54
|
+
animation: `${fadeInPickerBackground} ${fadeDuration}ms 1 both`
|
|
55
|
+
}).toString(),
|
|
56
|
+
inactive: css({
|
|
57
|
+
animation: `${fadeOutPickerBackground} ${fadeDuration}ms 1 both`
|
|
58
|
+
}).toString()
|
|
59
|
+
};
|
|
60
|
+
const container = {
|
|
61
|
+
base: css({
|
|
62
|
+
position: 'absolute',
|
|
63
|
+
bottom: 0,
|
|
64
|
+
left: 0,
|
|
65
|
+
right: 0,
|
|
66
|
+
backgroundColor: themeColors.light,
|
|
67
|
+
animation: `${slideInPickerModal} ${slideDuration}ms 1 both ${easing}`
|
|
68
|
+
}).toString(),
|
|
69
|
+
inactive: css({
|
|
70
|
+
animation: `${slideOutPickerModal} ${slideDuration}ms 1 both ${easing}`
|
|
71
|
+
}).toString()
|
|
72
|
+
};
|
|
73
|
+
export default {
|
|
74
|
+
background,
|
|
75
|
+
container,
|
|
76
|
+
wrapper,
|
|
77
|
+
duration: Math.max(slideDuration, fadeDuration)
|
|
78
|
+
};
|
|
@@ -1,40 +1,186 @@
|
|
|
1
|
-
|
|
1
|
+
import React, { Component } from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import { ConnectedReactPortal } from '@shopgate/engage/components';
|
|
4
|
+
import { withForwardedRef } from '@shopgate/engage/core';
|
|
5
|
+
import PickerModal from "./components/Modal";
|
|
6
|
+
import PickerButton from "./components/Button";
|
|
7
|
+
import PickerList from "./components/List";
|
|
8
|
+
|
|
9
|
+
/**
|
|
2
10
|
* Converts an item of any type (e.g. string or number)
|
|
3
11
|
* to an object representation containing value and label properties.
|
|
4
12
|
* @param {*} item - An item of any type.
|
|
5
13
|
* @returns {Object} An object representation of the item.
|
|
6
14
|
* @deprecated Use Picker from engage instead
|
|
7
|
-
*/
|
|
15
|
+
*/
|
|
16
|
+
const normalizeItem = item => {
|
|
17
|
+
if (item !== null && typeof item !== 'undefined') {
|
|
18
|
+
const itemDefaults = {
|
|
19
|
+
value: item.value || item,
|
|
20
|
+
label: item.label || item.value || item,
|
|
21
|
+
disabled: item.disabled || false
|
|
22
|
+
};
|
|
23
|
+
if (typeof item === 'object') {
|
|
24
|
+
return {
|
|
25
|
+
...itemDefaults,
|
|
26
|
+
...item
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
return itemDefaults;
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
8
35
|
* Finds an item index in a list of items by value.
|
|
9
36
|
* @param {Array} items - The list of items.
|
|
10
37
|
* @param {*} value - The value to look for.
|
|
11
38
|
* @returns {number} The found item index or undefined.
|
|
12
|
-
*/
|
|
39
|
+
*/
|
|
40
|
+
const findItemIndexByValue = (items, value) => items.map(normalizeItem).findIndex(item => item.value === value);
|
|
41
|
+
|
|
42
|
+
/**
|
|
13
43
|
* The picker component (acts like a selectbox).
|
|
14
44
|
* @deprecated Will be remove in Engage v7.0.0.
|
|
15
45
|
* Please use `import { Picker } from '@shopgate/engage/components'` instead.
|
|
16
|
-
*/
|
|
46
|
+
*/
|
|
47
|
+
class Picker extends Component {
|
|
48
|
+
/**
|
|
17
49
|
* The constructor.
|
|
18
50
|
* @param {Object} props - The component props.
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
51
|
+
*/
|
|
52
|
+
constructor(props) {
|
|
53
|
+
super(props);
|
|
54
|
+
/**
|
|
55
|
+
* Triggers the onChange callback if the selected value has changed.
|
|
56
|
+
* @param {Object} nextValue - The value the user picked.
|
|
57
|
+
*/
|
|
58
|
+
this.triggerChangeCallback = nextValue => {
|
|
59
|
+
const nextSelectedIndex = findItemIndexByValue(this.props.items, nextValue);
|
|
60
|
+
if (nextSelectedIndex > -1 && this.state.selectedIndex !== nextSelectedIndex) {
|
|
61
|
+
this.props.onChange(nextValue);
|
|
62
|
+
}
|
|
63
|
+
this.props.onSelect(nextValue);
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Triggers the onChange callback if the selected value has changed.
|
|
67
|
+
* @param {Object} nextOpenState - The value the user picked.
|
|
68
|
+
*/
|
|
69
|
+
this.triggerCloseCallback = nextOpenState => {
|
|
70
|
+
if (this.state.isOpen && !nextOpenState) {
|
|
71
|
+
this.props.onClose();
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* Gets called when a new item is selected
|
|
76
|
+
* @param {string} value - The selected value.
|
|
77
|
+
*/
|
|
78
|
+
this.handleItemSelect = value => {
|
|
79
|
+
this.triggerChangeCallback(value);
|
|
80
|
+
this.setState({
|
|
81
|
+
selectedIndex: findItemIndexByValue(this.props.items, value)
|
|
82
|
+
});
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* Sets or toggles the open state of the component.
|
|
86
|
+
* @param {boolean} [open] New open state.
|
|
87
|
+
*/
|
|
88
|
+
this.toggleOpenState = open => {
|
|
89
|
+
this.setState(({
|
|
90
|
+
isOpen
|
|
91
|
+
}) => {
|
|
92
|
+
const nextIsOpen = typeof open === 'boolean' ? open : !isOpen;
|
|
93
|
+
this.triggerCloseCallback(nextIsOpen);
|
|
94
|
+
return {
|
|
95
|
+
isOpen: nextIsOpen
|
|
96
|
+
};
|
|
97
|
+
});
|
|
98
|
+
};
|
|
99
|
+
this.state = {
|
|
100
|
+
selectedIndex: findItemIndexByValue(props.items, props.value),
|
|
101
|
+
isOpen: props.isOpen
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
32
106
|
* Updates the selected item when the value prop changes.
|
|
33
107
|
* @param {Object} nextProps - The next props.
|
|
34
|
-
*/
|
|
35
|
-
|
|
108
|
+
*/
|
|
109
|
+
UNSAFE_componentWillReceiveProps(nextProps) {
|
|
110
|
+
// Only update if a value is present and also changed.
|
|
111
|
+
if (!this.selectedItem || nextProps.value !== this.selectedItem.value) {
|
|
112
|
+
this.setState({
|
|
113
|
+
selectedIndex: findItemIndexByValue(nextProps.items, nextProps.value)
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
if (this.props.isOpen !== nextProps.isOpen && nextProps.isOpen !== this.state.isOpen) {
|
|
117
|
+
this.setState({
|
|
118
|
+
isOpen: nextProps.isOpen
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
36
124
|
* Getter for the selected item.
|
|
37
|
-
*/
|
|
125
|
+
*/
|
|
126
|
+
get selectedItem() {
|
|
127
|
+
return normalizeItem(this.props.items[this.state.selectedIndex]);
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
38
130
|
* Renders the component.
|
|
39
131
|
* @returns {JSX}
|
|
40
|
-
*/
|
|
132
|
+
*/
|
|
133
|
+
render() {
|
|
134
|
+
const hasSelection = !!this.selectedItem;
|
|
135
|
+
const buttonValue = hasSelection ? this.selectedItem.label : '';
|
|
136
|
+
const buttonLabel = hasSelection ? this.props.label : this.props.placeholder;
|
|
137
|
+
const buttonProps = {
|
|
138
|
+
...this.props.buttonProps,
|
|
139
|
+
label: buttonLabel,
|
|
140
|
+
value: buttonValue,
|
|
141
|
+
disabled: this.props.disabled,
|
|
142
|
+
openList: this.props.disabled ? null : () => {
|
|
143
|
+
this.toggleOpenState(true);
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
const modalProps = {
|
|
147
|
+
isOpen: this.state.isOpen,
|
|
148
|
+
duration: this.props.duration,
|
|
149
|
+
onClose: () => this.toggleOpenState(false)
|
|
150
|
+
};
|
|
151
|
+
const listProps = {
|
|
152
|
+
items: this.props.items.map(normalizeItem),
|
|
153
|
+
selectedIndex: this.state.selectedIndex,
|
|
154
|
+
onSelect: this.handleItemSelect
|
|
155
|
+
};
|
|
156
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
157
|
+
role: "button",
|
|
158
|
+
className: `${this.props.className} common__picker`,
|
|
159
|
+
ref: this.props.forwardedRef,
|
|
160
|
+
tabIndex: 0,
|
|
161
|
+
"aria-haspopup": true
|
|
162
|
+
}, /*#__PURE__*/React.createElement(this.props.buttonComponent, buttonProps), /*#__PURE__*/React.createElement(ConnectedReactPortal, {
|
|
163
|
+
onClose: () => this.toggleOpenState(false),
|
|
164
|
+
isOpened: true
|
|
165
|
+
}, /*#__PURE__*/React.createElement(this.props.modalComponent, modalProps, /*#__PURE__*/React.createElement(this.props.listComponent, listProps))));
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
Picker.defaultProps = {
|
|
169
|
+
buttonComponent: PickerButton,
|
|
170
|
+
buttonProps: {},
|
|
171
|
+
duration: 300,
|
|
172
|
+
disabled: false,
|
|
173
|
+
forwardedRef: null,
|
|
174
|
+
label: '',
|
|
175
|
+
listComponent: PickerList,
|
|
176
|
+
modalComponent: PickerModal,
|
|
177
|
+
className: '',
|
|
178
|
+
isOpen: false,
|
|
179
|
+
items: [],
|
|
180
|
+
onChange: () => {},
|
|
181
|
+
onClose: () => {},
|
|
182
|
+
onSelect: () => {},
|
|
183
|
+
placeholder: 'Pick ...',
|
|
184
|
+
value: null
|
|
185
|
+
};
|
|
186
|
+
export default withForwardedRef(Picker);
|
|
@@ -1,4 +1,85 @@
|
|
|
1
|
-
import React from'react';
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { mount } from 'enzyme';
|
|
3
|
+
import Picker from "./index";
|
|
4
|
+
import PickerList from "./components/List";
|
|
5
|
+
jest.mock('@shopgate/engage/components');
|
|
6
|
+
jest.mock('@shopgate/engage/core');
|
|
7
|
+
describe('<Picker />', () => {
|
|
8
|
+
let renderedElement;
|
|
9
|
+
let renderedInstance;
|
|
10
|
+
const mockItems = ['String only', {
|
|
11
|
+
value: 'Value only'
|
|
12
|
+
}, {
|
|
13
|
+
value: 'value',
|
|
14
|
+
label: 'Value and label'
|
|
15
|
+
}];
|
|
16
|
+
const mockOnChange = jest.fn();
|
|
17
|
+
const mockOnSelect = jest.fn();
|
|
18
|
+
|
|
19
|
+
/**
|
|
2
20
|
* The view component
|
|
3
21
|
* @param {Object} props The component props.
|
|
4
|
-
*/
|
|
22
|
+
*/
|
|
23
|
+
const renderComponent = props => {
|
|
24
|
+
renderedElement = mount(/*#__PURE__*/React.createElement(Picker, props));
|
|
25
|
+
renderedInstance = renderedElement.find('Picker').instance();
|
|
26
|
+
};
|
|
27
|
+
beforeEach(() => {
|
|
28
|
+
renderComponent({
|
|
29
|
+
items: mockItems,
|
|
30
|
+
onChange: mockOnChange,
|
|
31
|
+
onSelect: mockOnSelect
|
|
32
|
+
});
|
|
33
|
+
renderedElement.update();
|
|
34
|
+
});
|
|
35
|
+
describe('Given the component was mounted to the DOM', () => {
|
|
36
|
+
it('should match snapshot', () => {
|
|
37
|
+
expect(renderedElement).toMatchSnapshot();
|
|
38
|
+
});
|
|
39
|
+
it('should have no selected value', () => {
|
|
40
|
+
expect(renderedInstance.selectedItem).toBe(null);
|
|
41
|
+
});
|
|
42
|
+
describe('Given picker component gets opened', () => {
|
|
43
|
+
beforeEach(() => {
|
|
44
|
+
renderedInstance.toggleOpenState(true);
|
|
45
|
+
renderedElement.update();
|
|
46
|
+
});
|
|
47
|
+
it('should have isOpen state', () => {
|
|
48
|
+
expect(renderedInstance.state.isOpen).toBe(true);
|
|
49
|
+
});
|
|
50
|
+
it('should render the picker list', () => {
|
|
51
|
+
expect(renderedElement.find(PickerList).length).toBe(1);
|
|
52
|
+
});
|
|
53
|
+
it('should render the picker items', () => {
|
|
54
|
+
expect(renderedElement.find('li button').length).toBe(mockItems.length);
|
|
55
|
+
});
|
|
56
|
+
describe('Given a item gets selected', () => {
|
|
57
|
+
jest.useFakeTimers();
|
|
58
|
+
beforeEach(() => {
|
|
59
|
+
jest.clearAllMocks();
|
|
60
|
+
renderedElement.find('li button').first().simulate('click');
|
|
61
|
+
jest.runAllTimers();
|
|
62
|
+
});
|
|
63
|
+
afterEach(() => {
|
|
64
|
+
jest.clearAllTimers();
|
|
65
|
+
});
|
|
66
|
+
it('should trigger onChange when the value changed', () => {
|
|
67
|
+
expect(mockOnChange).toHaveBeenCalledTimes(1);
|
|
68
|
+
});
|
|
69
|
+
it('should trigger onSelect when the value changed', () => {
|
|
70
|
+
expect(mockOnSelect).toHaveBeenCalledTimes(1);
|
|
71
|
+
});
|
|
72
|
+
it('should not trigger onChange when the value did not change, but trigger onSelect', () => {
|
|
73
|
+
renderedInstance.setState({
|
|
74
|
+
selectedIndex: 0
|
|
75
|
+
});
|
|
76
|
+
renderedInstance.toggleOpenState(true);
|
|
77
|
+
renderedElement.find('li button').first().simulate('click');
|
|
78
|
+
jest.runAllTimers();
|
|
79
|
+
expect(mockOnChange).toHaveBeenCalledTimes(1);
|
|
80
|
+
expect(mockOnSelect).toHaveBeenCalledTimes(2);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
});
|
|
@@ -1,33 +1,144 @@
|
|
|
1
|
-
|
|
1
|
+
import React, { PureComponent, Suspense } from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import { logger } from '@shopgate/pwa-core/helpers';
|
|
4
|
+
import Loading from "../Loading";
|
|
5
|
+
import portalCollection from "../../helpers/portals/portalCollection";
|
|
6
|
+
import { componentsConfig } from "../../helpers/config";
|
|
7
|
+
|
|
8
|
+
/**
|
|
2
9
|
* The Portal component.
|
|
3
|
-
*/
|
|
10
|
+
*/
|
|
11
|
+
class Portal extends PureComponent {
|
|
12
|
+
/**
|
|
4
13
|
* Constructor.
|
|
5
14
|
* @param {Object} props The component props.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
*/
|
|
16
|
+
constructor(_props) {
|
|
17
|
+
super(_props);
|
|
18
|
+
/**
|
|
19
|
+
* Returns the portal components.
|
|
20
|
+
* @param {Object} props - The props to pass to the component.
|
|
21
|
+
* @return {Array}
|
|
22
|
+
*/
|
|
23
|
+
this.getRenderedComponents = props => {
|
|
24
|
+
const {
|
|
25
|
+
props: propsFromProps,
|
|
26
|
+
...reducedProps
|
|
27
|
+
} = props;
|
|
28
|
+
const componentProps = {
|
|
29
|
+
...propsFromProps,
|
|
30
|
+
...reducedProps
|
|
31
|
+
};
|
|
32
|
+
return this.components.map(({
|
|
33
|
+
PortalComponent,
|
|
34
|
+
key
|
|
35
|
+
}) => /*#__PURE__*/React.createElement(Suspense, {
|
|
36
|
+
fallback: /*#__PURE__*/React.createElement(Loading, null),
|
|
37
|
+
key: key
|
|
38
|
+
}, /*#__PURE__*/React.createElement(PortalComponent, componentProps)));
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Returns the portal components.
|
|
42
|
+
* @param {string} name Name of the portal position
|
|
43
|
+
* @return {Array}
|
|
44
|
+
*/
|
|
45
|
+
this.getPortalComponents = name => {
|
|
46
|
+
const components = [];
|
|
47
|
+
const portals = portalCollection.getPortals();
|
|
48
|
+
if (!portals) {
|
|
49
|
+
return components;
|
|
50
|
+
}
|
|
51
|
+
let config = portalCollection.getConfig();
|
|
52
|
+
if (!config) {
|
|
53
|
+
config = componentsConfig.portals;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Loop over the portal keys.
|
|
57
|
+
Object.keys(config).forEach((key, index) => {
|
|
58
|
+
const {
|
|
59
|
+
target: sourceTarget
|
|
60
|
+
} = config[key];
|
|
61
|
+
const portalTarget = Array.isArray(sourceTarget) ? sourceTarget : [sourceTarget];
|
|
62
|
+
if (portalTarget.length === 0) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
portalTarget.forEach(target => {
|
|
66
|
+
// Stop if there is no key that matches the given name (prop).
|
|
67
|
+
if (target !== name) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const PortalComponent = portals[key];
|
|
71
|
+
|
|
72
|
+
// Check that the component is valid.
|
|
73
|
+
if (PortalComponent) {
|
|
74
|
+
const componentKey = `${key}-${index}`;
|
|
75
|
+
components.push({
|
|
76
|
+
key: componentKey,
|
|
77
|
+
PortalComponent
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
return components;
|
|
83
|
+
};
|
|
84
|
+
this.state = {
|
|
85
|
+
hasError: false
|
|
86
|
+
};
|
|
87
|
+
this.components = this.getPortalComponents(_props.name);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
18
91
|
* Catches errors.
|
|
19
92
|
* @param {Error} error The caught error.
|
|
20
93
|
* @param {Object} info The stacktrace info.
|
|
21
|
-
*/
|
|
94
|
+
*/
|
|
95
|
+
componentDidCatch(error, info) {
|
|
96
|
+
this.setState({
|
|
97
|
+
hasError: true
|
|
98
|
+
});
|
|
99
|
+
logger.error(error, info);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
22
102
|
* Renders the component.
|
|
23
103
|
* @return {JSX}
|
|
24
|
-
*/
|
|
104
|
+
*/
|
|
105
|
+
render() {
|
|
106
|
+
const {
|
|
107
|
+
children
|
|
108
|
+
} = this.props;
|
|
109
|
+
const {
|
|
110
|
+
hasError
|
|
111
|
+
} = this.state;
|
|
112
|
+
const renderedComponents = this.getRenderedComponents(this.props);
|
|
113
|
+
const hasComponents = renderedComponents.length > 0;
|
|
114
|
+
|
|
115
|
+
/**
|
|
25
116
|
* Render nothing if there are no children, matching components
|
|
26
117
|
* via name or an error occurred.
|
|
27
|
-
*/
|
|
118
|
+
*/
|
|
119
|
+
if (hasError || !(hasComponents || children)) {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
28
124
|
* If there is a child component then we treat the match as an override
|
|
29
125
|
* and we render the last match only.
|
|
30
|
-
*/
|
|
126
|
+
*/
|
|
127
|
+
if (hasComponents) {
|
|
128
|
+
/**
|
|
31
129
|
* If there is a child component then we treat the match as an override
|
|
32
130
|
* and we render the last match only.
|
|
33
|
-
*/
|
|
131
|
+
*/
|
|
132
|
+
if (children) {
|
|
133
|
+
return renderedComponents[renderedComponents.length - 1];
|
|
134
|
+
}
|
|
135
|
+
return renderedComponents;
|
|
136
|
+
}
|
|
137
|
+
return children;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
Portal.defaultProps = {
|
|
141
|
+
children: null,
|
|
142
|
+
props: null
|
|
143
|
+
};
|
|
144
|
+
export default Portal;
|