@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,57 @@
|
|
|
1
|
-
|
|
1
|
+
import React, { Component } from 'react';
|
|
2
|
+
import isEqual from 'lodash/isEqual';
|
|
3
|
+
import PropTypes from 'prop-types';
|
|
4
|
+
import sortBy from 'lodash/sortBy';
|
|
5
|
+
import Widget from "../Widget";
|
|
6
|
+
import styles from "./style";
|
|
7
|
+
import shouldShowWidget from "../../helpers/shouldShowWidget";
|
|
8
|
+
|
|
9
|
+
/**
|
|
2
10
|
* The WidgetGrid component.
|
|
3
|
-
*/
|
|
11
|
+
*/
|
|
12
|
+
class WidgetGrid extends Component {
|
|
13
|
+
/**
|
|
4
14
|
* @param {Object} nextProps The next component props.
|
|
5
15
|
* @return {boolean}
|
|
6
|
-
*/
|
|
16
|
+
*/
|
|
17
|
+
shouldComponentUpdate(nextProps) {
|
|
18
|
+
if (!isEqual(this.props.components, nextProps.components)) return true;
|
|
19
|
+
if (!isEqual(this.props.config, nextProps.config)) return true;
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
7
24
|
* Render the component.
|
|
8
25
|
* @return {JSX}
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
26
|
+
*/
|
|
27
|
+
render() {
|
|
28
|
+
const {
|
|
29
|
+
components,
|
|
30
|
+
config
|
|
31
|
+
} = this.props;
|
|
32
|
+
if (!config.length) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// TODO: This should not happen within every render call.
|
|
37
|
+
// Sort the widgets by row. This has to happen to take care of the z-index flow.
|
|
38
|
+
const widgets = sortBy(config, ['row']).filter(w => shouldShowWidget(w.settings));
|
|
39
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
40
|
+
className: `${styles} common__widgets__widget-grid`
|
|
41
|
+
}, Object.keys(widgets).map(key => {
|
|
42
|
+
const widget = widgets[key];
|
|
43
|
+
const widgetKey = `w${key}`;
|
|
44
|
+
// Map to the correct widget component using the `type` key inside the widget.
|
|
45
|
+
const WidgetComponent = components[widget.type];
|
|
46
|
+
return /*#__PURE__*/React.createElement(Widget, {
|
|
47
|
+
config: widget,
|
|
48
|
+
component: WidgetComponent,
|
|
49
|
+
key: widgetKey
|
|
50
|
+
});
|
|
51
|
+
}));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
WidgetGrid.defaultProps = {
|
|
55
|
+
config: []
|
|
56
|
+
};
|
|
57
|
+
export default WidgetGrid;
|
|
@@ -1,4 +1,48 @@
|
|
|
1
|
-
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { mount } from 'enzyme';
|
|
3
|
+
import WidgetGrid from "./index";
|
|
4
|
+
jest.mock('react', () => ({
|
|
5
|
+
...require.requireActual('react'),
|
|
6
|
+
Suspense: function Suspense({
|
|
7
|
+
children
|
|
8
|
+
}) {
|
|
9
|
+
return children;
|
|
10
|
+
}
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
/**
|
|
2
14
|
* A mock Image component.
|
|
3
15
|
* @returns {JSX}
|
|
4
|
-
*/
|
|
16
|
+
*/
|
|
17
|
+
const Image = () => /*#__PURE__*/React.createElement("div", null);
|
|
18
|
+
const components = {
|
|
19
|
+
'@shopgate/commerce-widgets/image': Image
|
|
20
|
+
};
|
|
21
|
+
describe('<WidgetGrid />', () => {
|
|
22
|
+
it('should render with a config', () => {
|
|
23
|
+
const config = [{
|
|
24
|
+
col: 0,
|
|
25
|
+
row: 0,
|
|
26
|
+
width: 12,
|
|
27
|
+
height: 3,
|
|
28
|
+
settings: {
|
|
29
|
+
id: 83535,
|
|
30
|
+
image: 'https://data.shopgate.com/shop_widget_images/23836/92204c0f264ac30d6836994c2fb64eb1.min.jpeg'
|
|
31
|
+
},
|
|
32
|
+
type: '@shopgate/commerce-widgets/image'
|
|
33
|
+
}];
|
|
34
|
+
const wrapper = mount(/*#__PURE__*/React.createElement(WidgetGrid, {
|
|
35
|
+
config: config,
|
|
36
|
+
components: components
|
|
37
|
+
}));
|
|
38
|
+
expect(wrapper).toMatchSnapshot();
|
|
39
|
+
expect(wrapper.find(Image).length).toEqual(1);
|
|
40
|
+
});
|
|
41
|
+
it('should not render without a `config` prop', () => {
|
|
42
|
+
const wrapper = mount(/*#__PURE__*/React.createElement(WidgetGrid, {
|
|
43
|
+
components: components
|
|
44
|
+
}));
|
|
45
|
+
expect(wrapper).toMatchSnapshot();
|
|
46
|
+
expect(wrapper.find(Image).length).toEqual(0);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
@@ -1 +1,8 @@
|
|
|
1
|
-
import{css}from'glamor';
|
|
1
|
+
import { css } from 'glamor';
|
|
2
|
+
export default css({
|
|
3
|
+
display: 'grid',
|
|
4
|
+
gridTemplateColumns: 'repeat(12, 1fr)',
|
|
5
|
+
gridAutoRows: 'auto',
|
|
6
|
+
gridAutoFlow: 'row dense',
|
|
7
|
+
position: 'relative'
|
|
8
|
+
});
|
|
@@ -2,10 +2,47 @@
|
|
|
2
2
|
* Checks widget setting and decides if widget should be shown at the moment.
|
|
3
3
|
* @param {Object} settings Widget setting object.
|
|
4
4
|
* @returns {boolean}
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
if(
|
|
10
|
-
|
|
11
|
-
|
|
5
|
+
*/
|
|
6
|
+
function shouldShowWidget(settings = {}) {
|
|
7
|
+
const nowDate = new Date();
|
|
8
|
+
// Show widget if flag does not exist (old widgets)
|
|
9
|
+
if (!settings.hasOwnProperty('published')) {
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
if (settings.published === false) {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Defensive here since this data comes from the pipeline, it might be invalid for some reasons.
|
|
17
|
+
if (settings.hasOwnProperty('plan') && settings.plan) {
|
|
18
|
+
let startDate = null;
|
|
19
|
+
let endDate = null;
|
|
20
|
+
let notStartedYet = false;
|
|
21
|
+
let finishedAlready = false;
|
|
22
|
+
if (settings.planDate.valid_from) {
|
|
23
|
+
startDate = new Date(settings.planDate.valid_from);
|
|
24
|
+
notStartedYet = nowDate <= startDate;
|
|
25
|
+
}
|
|
26
|
+
if (settings.planDate.valid_to) {
|
|
27
|
+
endDate = new Date(settings.planDate.valid_to);
|
|
28
|
+
finishedAlready = nowDate >= endDate;
|
|
29
|
+
}
|
|
30
|
+
// Don't hide if no dates found
|
|
31
|
+
if (!startDate && !endDate) {
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Hide if some wrong dates are passed
|
|
36
|
+
if (startDate && endDate && startDate >= endDate) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Hide if start date is set but it is not there yet
|
|
41
|
+
// Hide if end date is reached
|
|
42
|
+
if (startDate && notStartedYet || endDate && finishedAlready) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
export default shouldShowWidget;
|
|
@@ -1,36 +1,148 @@
|
|
|
1
|
-
|
|
1
|
+
import React, { Component } from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import isEqual from 'lodash/isEqual';
|
|
4
|
+
import { withThemeWidgets } from '@shopgate/engage/core/hocs';
|
|
5
|
+
import WidgetGrid from "./components/WidgetGrid";
|
|
6
|
+
import shouldShowWidget from "./helpers/shouldShowWidget";
|
|
7
|
+
const WIDGET_GRID_TYPE = '@shopgate/commerce-widgets/widget-grid';
|
|
8
|
+
const GRID_COLUMNS = 12; // One grid row has 12 columns. // TODO: is it deprecated since css grid?
|
|
9
|
+
|
|
2
10
|
/**
|
|
3
11
|
* Creates a grid wrapper for widget(s).
|
|
4
12
|
* @param {string} key The unique key.
|
|
5
13
|
* @param {Array} config Array of widgets to be placed in the grid.
|
|
6
14
|
* @param {Array} components The component definitions for the widgets.
|
|
7
15
|
* @returns {JSX} The wrapper.
|
|
8
|
-
*/
|
|
16
|
+
*/
|
|
17
|
+
const createGridWrapper = (key, config, components) => (/*#__PURE__*/React.createElement(WidgetGrid, {
|
|
18
|
+
key,
|
|
19
|
+
config,
|
|
20
|
+
components
|
|
21
|
+
}));
|
|
22
|
+
|
|
23
|
+
/**
|
|
9
24
|
* The widgets component.
|
|
10
|
-
*/
|
|
25
|
+
*/
|
|
26
|
+
class Widgets extends Component {
|
|
27
|
+
/**
|
|
11
28
|
* @param {Object} props The component props.
|
|
12
|
-
*/
|
|
29
|
+
*/
|
|
30
|
+
constructor(props) {
|
|
31
|
+
super(props);
|
|
32
|
+
this.autoReloadInterval = undefined;
|
|
33
|
+
if (this.hasSchedulableWidgets()) {
|
|
34
|
+
this.startAutoRerender();
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
13
39
|
* @param {Object} nextProps The next component props.
|
|
14
40
|
* @return {JSX}
|
|
15
|
-
*/
|
|
41
|
+
*/
|
|
42
|
+
shouldComponentUpdate(nextProps) {
|
|
43
|
+
if (!isEqual(this.props.themeWidgets, nextProps.themeWidgets)) {
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
if (!isEqual(this.props.widgets, nextProps.widgets)) {
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
16
53
|
* Component will unmount lifecycle method.
|
|
17
|
-
*/
|
|
54
|
+
*/
|
|
55
|
+
componentWillUnmount() {
|
|
56
|
+
this.stopAutoRerender();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
18
60
|
* Checks if any widget is schedulable.
|
|
19
61
|
* @param {Array} widgets Array of widgets.
|
|
20
62
|
* @returns {boolean}
|
|
21
|
-
*/
|
|
63
|
+
*/
|
|
64
|
+
hasSchedulableWidgets() {
|
|
65
|
+
return (this.props.widgets || []).some(widget => widget.settings.plan);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
22
69
|
* Sets auto re-render.
|
|
23
|
-
*/
|
|
70
|
+
*/
|
|
71
|
+
startAutoRerender() {
|
|
72
|
+
const minutesToRoundedHour = 60 - new Date().getMinutes();
|
|
73
|
+
const nextRerenderIn = minutesToRoundedHour * 60000;
|
|
74
|
+
this.autoReloadTimeout = setTimeout(() => this.doAutoRerender(), nextRerenderIn);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
24
78
|
* Stops auto re-render. Must be called before component is unmounted to avoid
|
|
25
79
|
* memory leak.
|
|
26
|
-
*/
|
|
80
|
+
*/
|
|
81
|
+
stopAutoRerender() {
|
|
82
|
+
clearTimeout(this.autoReloadTimeout);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
27
86
|
* Forces re-render and sets another timeout for next cycle.
|
|
28
|
-
*/
|
|
87
|
+
*/
|
|
88
|
+
doAutoRerender() {
|
|
89
|
+
this.forceUpdate();
|
|
90
|
+
this.startAutoRerender();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
29
94
|
* Create array of elements from widget configuration.
|
|
30
95
|
* @returns {Array} Array of JSX elements.
|
|
31
|
-
*/
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
96
|
+
*/
|
|
97
|
+
createArrayOfElements() {
|
|
98
|
+
const {
|
|
99
|
+
widgets = [],
|
|
100
|
+
themeWidgets: components
|
|
101
|
+
} = this.props;
|
|
102
|
+
return widgets.filter(widget => shouldShowWidget(widget.settings)).map((widget, index) => {
|
|
103
|
+
if (!components[widget.type] && widget.type !== WIDGET_GRID_TYPE) {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
const key = `w${widget?.id || index}`;
|
|
107
|
+
if (widget.type === WIDGET_GRID_TYPE) {
|
|
108
|
+
// If it's a grid just create it and pass the child widgets.
|
|
109
|
+
return createGridWrapper(key, widget.settings.widgets, components);
|
|
110
|
+
}
|
|
111
|
+
if (widget.height) {
|
|
112
|
+
// If it has a definite height wrap the widget in a grid.
|
|
113
|
+
return createGridWrapper(key, [{
|
|
114
|
+
...widget,
|
|
115
|
+
col: 0,
|
|
116
|
+
row: 0,
|
|
117
|
+
width: GRID_COLUMNS
|
|
118
|
+
}], components);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// In all other cases just create and return the widget component.
|
|
122
|
+
return /*#__PURE__*/React.createElement(components[widget.type], {
|
|
123
|
+
...widget,
|
|
124
|
+
key
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
35
130
|
* @return {JSX}
|
|
36
|
-
*/
|
|
131
|
+
*/
|
|
132
|
+
render() {
|
|
133
|
+
const {
|
|
134
|
+
widgets,
|
|
135
|
+
themeWidgets: components
|
|
136
|
+
} = this.props;
|
|
137
|
+
if (!widgets) {
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
141
|
+
className: "common__widgets"
|
|
142
|
+
}, this.createArrayOfElements(widgets, components));
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
Widgets.defaultProps = {
|
|
146
|
+
widgets: null
|
|
147
|
+
};
|
|
148
|
+
export default withThemeWidgets(Widgets);
|
|
@@ -1,13 +1,220 @@
|
|
|
1
|
-
import React from'react';
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { mount } from 'enzyme';
|
|
3
|
+
import { ThemeResourcesProvider } from '@shopgate/engage/core/providers';
|
|
4
|
+
import Widgets from "./index";
|
|
5
|
+
jest.useFakeTimers();
|
|
6
|
+
jest.mock('@shopgate/pwa-common/context', () => ({
|
|
7
|
+
ThemeContext: {
|
|
8
|
+
Provider: ({
|
|
9
|
+
children
|
|
10
|
+
}) => children
|
|
11
|
+
}
|
|
12
|
+
}));
|
|
13
|
+
|
|
14
|
+
/**
|
|
2
15
|
* A mock Image component.
|
|
3
16
|
* @returns {JSX}
|
|
4
|
-
*/
|
|
17
|
+
*/
|
|
18
|
+
const Image = () => /*#__PURE__*/React.createElement("img", {
|
|
19
|
+
alt: ""
|
|
20
|
+
});
|
|
21
|
+
/* eslint-disable react/prop-types */
|
|
22
|
+
/**
|
|
5
23
|
* A mock WidgetGrid component.
|
|
6
24
|
* @param {Array} children Array of children.
|
|
7
25
|
* @returns {JSX}
|
|
8
|
-
*/
|
|
26
|
+
*/
|
|
27
|
+
const WidgetGrid = ({
|
|
28
|
+
children
|
|
29
|
+
}) => /*#__PURE__*/React.createElement("div", {
|
|
30
|
+
className: "widget-grid"
|
|
31
|
+
}, children);
|
|
32
|
+
/* eslint-enable react/prop-types */
|
|
33
|
+
|
|
34
|
+
const components = {
|
|
35
|
+
v1: {
|
|
36
|
+
'@shopgate/commerce-widgets/image': Image,
|
|
37
|
+
'@shopgate/commerce-widgets/widget-grid': WidgetGrid
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/**
|
|
9
42
|
* @param {Object[]} widgets Widgets to be rendered.
|
|
10
43
|
* @returns {JSX.Element}
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
|
|
44
|
+
*/
|
|
45
|
+
const createWrapper = widgets => mount(/*#__PURE__*/React.createElement(ThemeResourcesProvider, {
|
|
46
|
+
widgets: components,
|
|
47
|
+
components: {}
|
|
48
|
+
}, /*#__PURE__*/React.createElement(Widgets, {
|
|
49
|
+
widgets: widgets
|
|
50
|
+
})));
|
|
51
|
+
describe('<Widgets />', () => {
|
|
52
|
+
it('should render a grid if height is defined', () => {
|
|
53
|
+
const widgets = [{
|
|
54
|
+
col: 0,
|
|
55
|
+
row: 0,
|
|
56
|
+
width: 12,
|
|
57
|
+
height: 3,
|
|
58
|
+
settings: {
|
|
59
|
+
id: 83535,
|
|
60
|
+
image: 'https://data.shopgate.com/shop_widget_images/23836/92204c0f264ac30d6836994c2fb64eb1.min.jpeg'
|
|
61
|
+
},
|
|
62
|
+
type: '@shopgate/commerce-widgets/image'
|
|
63
|
+
}];
|
|
64
|
+
const wrapper = createWrapper(widgets);
|
|
65
|
+
expect(wrapper.find('WidgetGrid').exists()).toBe(true);
|
|
66
|
+
});
|
|
67
|
+
it('should not wrap a widget which is not a grid and has no height', () => {
|
|
68
|
+
const widgets = [{
|
|
69
|
+
col: 0,
|
|
70
|
+
row: 0,
|
|
71
|
+
width: 12,
|
|
72
|
+
settings: {
|
|
73
|
+
id: 83535,
|
|
74
|
+
image: 'https://data.shopgate.com/shop_widget_images/23836/92204c0f264ac30d6836994c2fb64eb1.min.jpeg'
|
|
75
|
+
},
|
|
76
|
+
type: '@shopgate/commerce-widgets/image'
|
|
77
|
+
}];
|
|
78
|
+
const wrapper = createWrapper(widgets);
|
|
79
|
+
expect(wrapper).toMatchSnapshot();
|
|
80
|
+
expect(wrapper.find('WidgetGrid').exists()).toBe(false);
|
|
81
|
+
});
|
|
82
|
+
it('should render a grid if the widget is of type grid', () => {
|
|
83
|
+
const widgets = [{
|
|
84
|
+
type: '@shopgate/commerce-widgets/widget-grid',
|
|
85
|
+
settings: {
|
|
86
|
+
widgets: [{
|
|
87
|
+
col: 0,
|
|
88
|
+
row: 0,
|
|
89
|
+
width: 12,
|
|
90
|
+
height: 5,
|
|
91
|
+
settings: {
|
|
92
|
+
id: '84961',
|
|
93
|
+
alt: '',
|
|
94
|
+
image: 'https://data.shopgate.com/shop_widget_images/23836/aedc545959f55e3f73851eca0ed40a75.min.jpeg',
|
|
95
|
+
link: '/category/'
|
|
96
|
+
},
|
|
97
|
+
type: '@shopgate/commerce-widgets/image'
|
|
98
|
+
}]
|
|
99
|
+
}
|
|
100
|
+
}];
|
|
101
|
+
const wrapper = createWrapper(widgets);
|
|
102
|
+
expect(wrapper).toMatchSnapshot();
|
|
103
|
+
expect(wrapper.find('WidgetGrid').exists()).toBe(true);
|
|
104
|
+
});
|
|
105
|
+
it('should render only one widget when the second one is not published and third one is invalid', () => {
|
|
106
|
+
const widgets = [{
|
|
107
|
+
col: 0,
|
|
108
|
+
row: 0,
|
|
109
|
+
width: 12,
|
|
110
|
+
settings: {
|
|
111
|
+
id: 835351,
|
|
112
|
+
image: 'https://data.shopgate.com/shop_widget_images/23836/92204c0f264ac30d6836994c2fb64eb1.min.jpeg'
|
|
113
|
+
},
|
|
114
|
+
type: '@shopgate/commerce-widgets/image'
|
|
115
|
+
}, {
|
|
116
|
+
col: 0,
|
|
117
|
+
row: 0,
|
|
118
|
+
width: 12,
|
|
119
|
+
settings: {
|
|
120
|
+
id: 835352,
|
|
121
|
+
image: 'https://data.shopgate.com/shop_widget_images/23836/92204c0f264ac30d6836994c2fb64eb1.min.jpeg'
|
|
122
|
+
},
|
|
123
|
+
type: '@shopgate/commerce-widgets/imagefoo'
|
|
124
|
+
}, {
|
|
125
|
+
col: 0,
|
|
126
|
+
row: 0,
|
|
127
|
+
width: 12,
|
|
128
|
+
settings: {
|
|
129
|
+
published: false,
|
|
130
|
+
id: 835353,
|
|
131
|
+
image: 'https://data.shopgate.com/shop_widget_images/23836/92204c0f264ac30d6836994c2fb64eb1.min.jpeg'
|
|
132
|
+
},
|
|
133
|
+
type: '@shopgate/commerce-widgets/image'
|
|
134
|
+
}];
|
|
135
|
+
const wrapper = createWrapper(widgets);
|
|
136
|
+
expect(wrapper).toMatchSnapshot();
|
|
137
|
+
expect(wrapper.find('img').length).toBe(1);
|
|
138
|
+
});
|
|
139
|
+
it('should schedule a re-render when widget is scheduled', () => {
|
|
140
|
+
const minutesToNextFullHour = 60 - new Date().getMinutes();
|
|
141
|
+
const msToNextFullHour = minutesToNextFullHour * 60000;
|
|
142
|
+
const scheduledFromMs = Date.now() + msToNextFullHour - 1;
|
|
143
|
+
const scheduledToMs = Date.now() + minutesToNextFullHour + 1000;
|
|
144
|
+
/* eslint-disable camelcase */
|
|
145
|
+
const widgets = [{
|
|
146
|
+
col: 0,
|
|
147
|
+
row: 0,
|
|
148
|
+
width: 12,
|
|
149
|
+
settings: {
|
|
150
|
+
id: 835351,
|
|
151
|
+
image: 'https://data.shopgate.com/shop_widget_images/23836/92204c0f264ac30d6836994c2fb64eb1.min.jpeg',
|
|
152
|
+
published: true,
|
|
153
|
+
plan: true,
|
|
154
|
+
planDate: {
|
|
155
|
+
valid_from: new Date(scheduledFromMs).toISOString(),
|
|
156
|
+
valid_to: new Date(scheduledToMs).toISOString()
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
type: '@shopgate/commerce-widgets/image'
|
|
160
|
+
}];
|
|
161
|
+
/* eslint-enable camelcase */
|
|
162
|
+
const wrapper = createWrapper(widgets);
|
|
163
|
+
const instance = wrapper.find('Widgets').instance();
|
|
164
|
+
instance.forceUpdate = jest.fn();
|
|
165
|
+
expect(wrapper.find(Image).exists()).toBe(false);
|
|
166
|
+
jest.advanceTimersByTime(msToNextFullHour);
|
|
167
|
+
expect(instance.forceUpdate).toHaveBeenCalledTimes(1);
|
|
168
|
+
// In real life next timeout should be in 60 minutes.
|
|
169
|
+
// This test has same Date and fake timers.
|
|
170
|
+
jest.advanceTimersByTime(msToNextFullHour);
|
|
171
|
+
expect(instance.forceUpdate).toHaveBeenCalledTimes(2);
|
|
172
|
+
instance.componentWillUnmount();
|
|
173
|
+
expect(clearTimeout).toHaveBeenCalled();
|
|
174
|
+
});
|
|
175
|
+
it('should render only wrapper when widgets array is empty', () => {
|
|
176
|
+
const widgets = [];
|
|
177
|
+
const wrapper = createWrapper(widgets);
|
|
178
|
+
expect(wrapper.find('Image').exists()).toBe(false);
|
|
179
|
+
});
|
|
180
|
+
it('should render null when no widgets are passed', () => {
|
|
181
|
+
const wrapper = createWrapper(undefined);
|
|
182
|
+
expect(wrapper.find('Widgets').html()).toBe(null);
|
|
183
|
+
});
|
|
184
|
+
it('should check settings of child widgets inside widget-grid', () => {
|
|
185
|
+
const widgets = [{
|
|
186
|
+
height: 2,
|
|
187
|
+
id: 'index-5-@shopgate/commerce-widgets/widget-grid',
|
|
188
|
+
type: '@shopgate/commerce-widgets/widget-grid',
|
|
189
|
+
settings: {
|
|
190
|
+
widgets: [{
|
|
191
|
+
col: 0,
|
|
192
|
+
row: 0,
|
|
193
|
+
height: 2,
|
|
194
|
+
width: 2,
|
|
195
|
+
settings: {
|
|
196
|
+
id: 835351,
|
|
197
|
+
image: 'https://data.shopgate.com/shop_widget_images/23836/92204c0f264ac30d6836994c2fb64eb1.min.jpeg',
|
|
198
|
+
published: true,
|
|
199
|
+
plan: false
|
|
200
|
+
},
|
|
201
|
+
type: '@shopgate/commerce-widgets/image'
|
|
202
|
+
}, {
|
|
203
|
+
col: 2,
|
|
204
|
+
row: 0,
|
|
205
|
+
height: 2,
|
|
206
|
+
width: 2,
|
|
207
|
+
settings: {
|
|
208
|
+
id: 835352,
|
|
209
|
+
image: 'https://data.shopgate.com/shop_widget_images/23836/92204c0f264ac30d6836994c2fb64eb1.min.jpeg',
|
|
210
|
+
published: false,
|
|
211
|
+
plan: false
|
|
212
|
+
},
|
|
213
|
+
type: '@shopgate/commerce-widgets/image'
|
|
214
|
+
}]
|
|
215
|
+
}
|
|
216
|
+
}];
|
|
217
|
+
const wrapper = createWrapper(widgets);
|
|
218
|
+
expect(wrapper.find('img').length).toBe(1);
|
|
219
|
+
});
|
|
220
|
+
});
|
package/components/index.js
CHANGED
|
@@ -1 +1,9 @@
|
|
|
1
|
-
export
|
|
1
|
+
export { default as Ellipsis } from "./Ellipsis";
|
|
2
|
+
export { default as Router } from "./Router";
|
|
3
|
+
export { default as Image } from "./Image";
|
|
4
|
+
export { default as Link } from "./Link";
|
|
5
|
+
export { default as Route } from "./Route";
|
|
6
|
+
export { default as Portal } from "./Portal";
|
|
7
|
+
export { default as Slider } from "./Slider";
|
|
8
|
+
export { default as Swiper } from "./Swiper";
|
|
9
|
+
export { default as SurroundPortals } from "./SurroundPortals";
|