@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,203 @@
|
|
|
1
|
-
|
|
1
|
+
import range from 'lodash/range';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { shallow, mount } from 'enzyme';
|
|
4
|
+
import { ITEMS_PER_LOAD } from "../../constants/DisplayOptions";
|
|
5
|
+
import InfiniteContainer from "./index";
|
|
6
|
+
global.console.error = jest.fn();
|
|
7
|
+
const context = {
|
|
8
|
+
state: {}
|
|
9
|
+
};
|
|
10
|
+
describe('<InfiniteContainer />', () => {
|
|
11
|
+
let renderedElement;
|
|
12
|
+
let renderedInstance;
|
|
13
|
+
let mockLoader;
|
|
14
|
+
let mockIterator;
|
|
15
|
+
let mockItems;
|
|
16
|
+
const mockData = range(100).map(id => ({
|
|
17
|
+
id,
|
|
18
|
+
title: `Item ${id}`
|
|
19
|
+
}));
|
|
20
|
+
|
|
21
|
+
/**
|
|
2
22
|
* The view component
|
|
3
23
|
* @param {Object} props The component props.
|
|
4
|
-
*/
|
|
24
|
+
*/
|
|
25
|
+
const renderComponent = props => {
|
|
26
|
+
renderedElement = shallow(/*#__PURE__*/React.createElement(InfiniteContainer, props), {
|
|
27
|
+
context
|
|
28
|
+
});
|
|
29
|
+
renderedInstance = renderedElement.instance();
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
5
33
|
* Mocks the mapStateToProps connector.
|
|
6
34
|
* @param {number} amount The new product amount.
|
|
7
|
-
*/
|
|
8
|
-
receiveItemsByProp
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
renderedInstance.
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
35
|
+
*/
|
|
36
|
+
const receiveItemsByProp = amount => {
|
|
37
|
+
mockItems = mockData.slice(0, amount);
|
|
38
|
+
const nextProps = {
|
|
39
|
+
...renderedInstance.props,
|
|
40
|
+
items: mockItems,
|
|
41
|
+
totalItems: mockData.length
|
|
42
|
+
};
|
|
43
|
+
renderedElement.setProps(nextProps);
|
|
44
|
+
};
|
|
45
|
+
beforeEach(() => {
|
|
46
|
+
mockLoader = jest.fn();
|
|
47
|
+
mockIterator = jest.fn(data => /*#__PURE__*/React.createElement("li", {
|
|
48
|
+
key: data.id
|
|
49
|
+
}, data.title));
|
|
50
|
+
});
|
|
51
|
+
describe('Given the component was mounted to the DOM', () => {
|
|
52
|
+
beforeEach(() => {
|
|
53
|
+
renderComponent({
|
|
54
|
+
items: [],
|
|
55
|
+
loader: mockLoader,
|
|
56
|
+
iterator: mockIterator,
|
|
57
|
+
totalItems: null
|
|
58
|
+
});
|
|
59
|
+
renderedInstance.componentDidMount();
|
|
60
|
+
});
|
|
61
|
+
it('should match snapshot', () => {
|
|
62
|
+
expect(renderedElement).toMatchSnapshot();
|
|
63
|
+
});
|
|
64
|
+
it('should call the loader function', () => {
|
|
65
|
+
const [offset] = renderedInstance.state.offset;
|
|
66
|
+
expect(mockLoader).toBeCalledWith(offset);
|
|
67
|
+
});
|
|
68
|
+
describe('Given the loader requested new items', () => {
|
|
69
|
+
const mockItemsLength = 10;
|
|
70
|
+
beforeEach(() => {
|
|
71
|
+
receiveItemsByProp(mockItemsLength);
|
|
72
|
+
});
|
|
73
|
+
it('should call the iterator function according to the number of loaded items', () => {
|
|
74
|
+
expect(mockIterator).toBeCalled();
|
|
75
|
+
expect(mockIterator.mock.calls.length).toBe(mockItemsLength);
|
|
76
|
+
});
|
|
77
|
+
it('should render the loaded items', () => {
|
|
78
|
+
expect(renderedElement.find('li').length).toBe(mockItemsLength);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
describe('Given the component was mounted within a scroll container', () => {
|
|
82
|
+
const mockItemsLength = 11;
|
|
83
|
+
beforeEach(() => {
|
|
84
|
+
// Receive items from initial mounting before proceeding...
|
|
85
|
+
receiveItemsByProp(mockItemsLength);
|
|
86
|
+
|
|
87
|
+
// Reset any previous calls (e.g. from componentDidMount())
|
|
88
|
+
mockLoader.mock.calls = [];
|
|
89
|
+
renderedInstance.componentDidUpdate();
|
|
90
|
+
});
|
|
91
|
+
it('should call the loader function if scrolled to the bottom', () => {
|
|
92
|
+
renderedInstance.domScrollContainer = {
|
|
93
|
+
scrollTop: 900,
|
|
94
|
+
scrollHeight: 1000,
|
|
95
|
+
clientHeight: 100
|
|
96
|
+
};
|
|
97
|
+
renderedInstance.handleLoading();
|
|
98
|
+
expect(mockLoader).toBeCalled();
|
|
99
|
+
});
|
|
100
|
+
it('should not call the loader function if the scroll position did not change', () => {
|
|
101
|
+
renderedInstance.domScrollContainer = {
|
|
102
|
+
scrollTop: 0,
|
|
103
|
+
scrollHeight: 1000,
|
|
104
|
+
clientHeight: 100
|
|
105
|
+
};
|
|
106
|
+
renderedInstance.handleLoading();
|
|
107
|
+
expect(mockLoader.mock.calls.length).toBe(0);
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
describe('Given all items have been received', () => {
|
|
111
|
+
const mockItemsLength = mockData.length;
|
|
112
|
+
beforeEach(() => {
|
|
113
|
+
receiveItemsByProp(mockItemsLength);
|
|
114
|
+
});
|
|
115
|
+
it('should expect no more items to be received', () => {
|
|
116
|
+
expect(renderedInstance.needsToReceiveItems()).toBe(false);
|
|
117
|
+
});
|
|
118
|
+
it('should keep state.awaitingItems as true if not all items are rendered', () => {
|
|
119
|
+
expect(renderedInstance.allItemsAreRendered()).toBe(false);
|
|
120
|
+
expect(renderedInstance.state.awaitingItems).toBe(true);
|
|
121
|
+
expect(renderedElement.find('li').length).toBeLessThan(mockItemsLength);
|
|
122
|
+
});
|
|
123
|
+
it('should set state.awaitingItems to false if all items are rendered', () => {
|
|
124
|
+
renderedElement.setState({
|
|
125
|
+
offset: [0, mockItemsLength]
|
|
126
|
+
});
|
|
127
|
+
renderedInstance.handleLoading();
|
|
128
|
+
expect(renderedInstance.allItemsAreRendered()).toBe(true);
|
|
129
|
+
expect(renderedInstance.state.awaitingItems).toBe(false);
|
|
130
|
+
expect(renderedElement.find('li').length).toBe(mockItemsLength);
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
describe('Given that the initialLimit is used in the correct ways', () => {
|
|
135
|
+
describe('Given that the initialLimit is used', () => {
|
|
136
|
+
it('should render with the initialLimit', () => {
|
|
137
|
+
renderComponent({
|
|
138
|
+
items: mockData,
|
|
139
|
+
loader: mockLoader,
|
|
140
|
+
iterator: mockIterator,
|
|
141
|
+
totalItems: mockData.length
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// Check if the iniLimit was used
|
|
145
|
+
expect(renderedElement.find('li').length).toBe(renderedInstance.props.initialLimit);
|
|
146
|
+
|
|
147
|
+
// Reset the limit from props.initialLimit back to props.limit
|
|
148
|
+
renderedInstance.componentDidMount();
|
|
149
|
+
renderedInstance.handleLoading();
|
|
150
|
+
|
|
151
|
+
// Re-render with the new limit
|
|
152
|
+
renderedElement.update();
|
|
153
|
+
|
|
154
|
+
// Check if the correct limit was used for the second render
|
|
155
|
+
const newLimit = renderedInstance.props.initialLimit + renderedInstance.props.limit;
|
|
156
|
+
expect(renderedElement.find('li').length).toBe(newLimit);
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
describe('Given that the initialLimit is NOT used', () => {
|
|
160
|
+
it('should render without the initialLimit', () => {
|
|
161
|
+
renderComponent({
|
|
162
|
+
items: [],
|
|
163
|
+
loader: mockLoader,
|
|
164
|
+
iterator: mockIterator,
|
|
165
|
+
totalItems: null
|
|
166
|
+
});
|
|
167
|
+
renderedInstance.componentDidMount();
|
|
168
|
+
receiveItemsByProp(ITEMS_PER_LOAD);
|
|
169
|
+
|
|
170
|
+
// Check if the iniLimit wasn't used
|
|
171
|
+
expect(renderedElement).toMatchSnapshot();
|
|
172
|
+
expect(renderedElement.find('li').length).toBe(renderedInstance.props.limit);
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
describe('Given that the requestHash changes', () => {
|
|
177
|
+
it('should reset the component', () => {
|
|
178
|
+
const props = {
|
|
179
|
+
items: mockData,
|
|
180
|
+
loader: mockLoader,
|
|
181
|
+
iterator: mockIterator,
|
|
182
|
+
totalItems: mockData.length,
|
|
183
|
+
requestHash: 'default'
|
|
184
|
+
};
|
|
185
|
+
const wrapper = mount(/*#__PURE__*/React.createElement(InfiniteContainer, props));
|
|
186
|
+
const instance = wrapper.instance();
|
|
187
|
+
instance.componentDidMount();
|
|
188
|
+
instance.domScrollContainer = document.createElement('div');
|
|
189
|
+
wrapper.setState({
|
|
190
|
+
awaitingItems: false,
|
|
191
|
+
offset: [10, 10]
|
|
192
|
+
});
|
|
193
|
+
wrapper.setProps({
|
|
194
|
+
requestHash: 'price_desc'
|
|
195
|
+
});
|
|
196
|
+
expect(wrapper.state()).toEqual({
|
|
197
|
+
offset: [0, 32],
|
|
198
|
+
awaitingItems: true,
|
|
199
|
+
itemCount: 0
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
});
|
|
@@ -1,17 +1,273 @@
|
|
|
1
|
-
|
|
1
|
+
import _extends from "@babel/runtime/helpers/extends";
|
|
2
|
+
import React, { useCallback, useState, useEffect, useRef } from 'react';
|
|
3
|
+
import PropTypes from 'prop-types';
|
|
4
|
+
import DayPicker from 'react-day-picker';
|
|
5
|
+
import MomentLocaleUtils from 'react-day-picker/moment';
|
|
6
|
+
import 'react-day-picker/lib/style.css';
|
|
7
|
+
import { css } from 'glamor';
|
|
8
|
+
import classNames from 'classnames';
|
|
9
|
+
import moment from 'moment';
|
|
10
|
+
import CalendarIcon from '@shopgate/pwa-ui-shared/icons/CalendarIcon';
|
|
11
|
+
import appConfig, { themeConfig } from '@shopgate/pwa-common/helpers/config';
|
|
12
|
+
import Backdrop from '@shopgate/pwa-common/components/Backdrop';
|
|
13
|
+
import { i18n } from '@shopgate/engage/core/helpers';
|
|
14
|
+
import SimpleInput from "./SimpleInput";
|
|
15
|
+
const {
|
|
16
|
+
variables
|
|
17
|
+
} = themeConfig;
|
|
18
|
+
const locale = appConfig.language.substring(0, 2);
|
|
19
|
+
|
|
20
|
+
/**
|
|
2
21
|
* @param {Object|string} date The input date
|
|
3
22
|
* @returns {string}
|
|
4
|
-
*/
|
|
23
|
+
*/
|
|
24
|
+
const getDateFromISO = date => {
|
|
25
|
+
const result = moment(date);
|
|
26
|
+
if (!result.isValid()) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
return result.toDate();
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
5
33
|
* @param {Object|string} date The input date
|
|
6
34
|
* @param {boolean} validate Should the date be validated
|
|
7
35
|
* @returns {string}
|
|
8
|
-
*/
|
|
36
|
+
*/
|
|
37
|
+
const getISOFormattedDate = (date, validate = true) => {
|
|
38
|
+
const result = moment(date, 'L', locale);
|
|
39
|
+
if (validate && !result.isValid()) {
|
|
40
|
+
return '';
|
|
41
|
+
}
|
|
42
|
+
return result.format(moment.HTML5_FMT.DATE);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/**
|
|
9
46
|
* @param {Object|string} date The input date
|
|
10
47
|
* @returns {string}
|
|
11
|
-
*/
|
|
48
|
+
*/
|
|
49
|
+
const getLocaleFormattedDate = date => {
|
|
50
|
+
const result = moment(date);
|
|
51
|
+
if (!result.isValid()) {
|
|
52
|
+
return '';
|
|
53
|
+
}
|
|
54
|
+
return result.format('L');
|
|
55
|
+
};
|
|
56
|
+
const styles = {
|
|
57
|
+
selectBox: css({
|
|
58
|
+
WebkitAppearance: 'none',
|
|
59
|
+
border: '1px solid var(--color-text-low-emphasis)',
|
|
60
|
+
padding: '2px 16px 2px 6px',
|
|
61
|
+
marginRight: 7,
|
|
62
|
+
backgroundImage: 'url(data:image/svg+xml;utf8;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pg0KPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2ZXJzaW9uPSIxLjEiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgNDA1LjQ1NiA0MDUuNDU2IiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA0MDUuNDU2IDQwNS40NTY7IiB4bWw6c3BhY2U9InByZXNlcnZlIiB3aWR0aD0iMTZweCIgaGVpZ2h0PSIxNnB4Ij4NCjxnPg0KCTxwYXRoIGQ9Ik03NC4xMzQsNjQuMTQ3Yy00Ljk4NSwwLjA3OC05LjkxMSwyLjE2My0xMy40MzgsNS42ODhsLTU1LDU1QzIuMDk2LDEyOC40MzIsMCwxMzMuNDkyLDAsMTM4LjU4MyAgIHMyLjA5NiwxMC4xNTEsNS42OTcsMTMuNzVsMTgzLjI4MSwxODMuMjgxYzMuNTk5LDMuNjAxLDguNjU5LDUuNjk3LDEzLjc1LDUuNjk3czEwLjE1MS0yLjA5NiwxMy43NS01LjY5N2wxODMuMjgxLTE4My4yODEgICBjMy42MDEtMy41OTksNS42OTctOC42NTksNS42OTctMTMuNzVzLTIuMDk2LTEwLjE1MS01LjY5Ny0xMy43NWwtNTUtNTVjLTMuNTk4LTMuNTkxLTguNjUxLTUuNjgxLTEzLjczNC01LjY4MSAgIGMtNS4wODMsMC0xMC4xMzYsMi4wOS0xMy43MzQsNS42ODFMMjAyLjcyOCwxODQuMzk3TDg4LjE2Niw2OS44MzNDODQuNDk5LDY2LjE2OSw3OS4zMTgsNjQuMDcsNzQuMTM0LDY0LjE0N0w3NC4xMzQsNjQuMTQ3eiIgZmlsbD0iIzk2OTY5NiIvPg0KPC9nPg0KPC9zdmc+DQo=)',
|
|
63
|
+
backgroundPosition: 'right 4px top 50%',
|
|
64
|
+
backgroundRepeat: 'no-repeat',
|
|
65
|
+
backgroundSize: 10,
|
|
66
|
+
borderRadius: 0,
|
|
67
|
+
outline: 0,
|
|
68
|
+
cursor: 'pointer'
|
|
69
|
+
}).toString(),
|
|
70
|
+
iconWrapper: css({
|
|
71
|
+
position: 'absolute',
|
|
72
|
+
right: variables.gap.small,
|
|
73
|
+
top: '50%',
|
|
74
|
+
cursor: 'pointer',
|
|
75
|
+
color: 'var(--color-text-high-emphasis)'
|
|
76
|
+
}).toString(),
|
|
77
|
+
pickerWrapper: css({
|
|
78
|
+
position: 'relative',
|
|
79
|
+
zIndex: 12
|
|
80
|
+
}).toString(),
|
|
81
|
+
picker: css({
|
|
82
|
+
left: 'inherit !important',
|
|
83
|
+
right: 5,
|
|
84
|
+
' .DayPicker-Day': {
|
|
85
|
+
padding: '3px !important'
|
|
86
|
+
},
|
|
87
|
+
' .DayPicker-Day--selected': {
|
|
88
|
+
backgroundColor: 'var(--color-primary) !important'
|
|
89
|
+
},
|
|
90
|
+
' .DayPicker-Footer': {
|
|
91
|
+
textAlign: 'center'
|
|
92
|
+
},
|
|
93
|
+
' .DayPicker-TodayButton': {
|
|
94
|
+
color: 'var(--color-primary) !important'
|
|
95
|
+
},
|
|
96
|
+
' .DayPicker-TodayButton:hover': {
|
|
97
|
+
backgroundColor: 'var(--color-primary) !important',
|
|
98
|
+
color: '#fff !important'
|
|
99
|
+
},
|
|
100
|
+
' .DayPicker-Caption': {
|
|
101
|
+
padding: '0 !important'
|
|
102
|
+
},
|
|
103
|
+
' .DayPicker-NavButton': {
|
|
104
|
+
right: '.5em !important'
|
|
105
|
+
}
|
|
106
|
+
}).toString(),
|
|
107
|
+
pickerTop: css({
|
|
108
|
+
bottom: 30
|
|
109
|
+
}).toString(),
|
|
110
|
+
hidden: css({
|
|
111
|
+
display: 'none'
|
|
112
|
+
}).toString()
|
|
113
|
+
};
|
|
114
|
+
const currentYear = new Date().getFullYear();
|
|
115
|
+
const fromMonth = new Date(currentYear - 100, 11);
|
|
116
|
+
const toMonth = new Date(currentYear + 10, 11);
|
|
117
|
+
|
|
118
|
+
/**
|
|
12
119
|
* @param {Object} props The components props
|
|
13
120
|
* @returns {JSX}
|
|
14
|
-
*/
|
|
121
|
+
*/
|
|
122
|
+
const Caption = ({
|
|
123
|
+
date,
|
|
124
|
+
localeUtils,
|
|
125
|
+
onChange
|
|
126
|
+
}) => {
|
|
127
|
+
const months = localeUtils.getMonths(locale);
|
|
128
|
+
const years = [];
|
|
129
|
+
for (let i = fromMonth.getFullYear(); i <= toMonth.getFullYear(); i += 1) {
|
|
130
|
+
years.push(i);
|
|
131
|
+
}
|
|
132
|
+
const handleChange = function handleChange(e) {
|
|
133
|
+
const {
|
|
134
|
+
year,
|
|
135
|
+
month
|
|
136
|
+
} = e.target.form;
|
|
137
|
+
onChange(new Date(year.value, month.value));
|
|
138
|
+
};
|
|
139
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
140
|
+
className: "DayPicker-Caption"
|
|
141
|
+
}, /*#__PURE__*/React.createElement("select", {
|
|
142
|
+
name: "month",
|
|
143
|
+
onChange: handleChange,
|
|
144
|
+
value: date.getMonth(),
|
|
145
|
+
className: styles.selectBox
|
|
146
|
+
}, months.map((month, i) => /*#__PURE__*/React.createElement("option", {
|
|
147
|
+
key: month,
|
|
148
|
+
value: i
|
|
149
|
+
}, month))), /*#__PURE__*/React.createElement("select", {
|
|
150
|
+
name: "year",
|
|
151
|
+
onChange: handleChange,
|
|
152
|
+
value: date.getFullYear(),
|
|
153
|
+
className: styles.selectBox
|
|
154
|
+
}, years.map(year => /*#__PURE__*/React.createElement("option", {
|
|
155
|
+
key: year,
|
|
156
|
+
value: year
|
|
157
|
+
}, year))));
|
|
158
|
+
};
|
|
159
|
+
/**
|
|
15
160
|
* @param {Object} props The components props
|
|
16
161
|
* @returns {JSX}
|
|
17
|
-
*/
|
|
162
|
+
*/
|
|
163
|
+
const DateInput = ({
|
|
164
|
+
onFocusChange,
|
|
165
|
+
onChange,
|
|
166
|
+
type,
|
|
167
|
+
value,
|
|
168
|
+
...rest
|
|
169
|
+
}) => {
|
|
170
|
+
const wrapperRef = useRef(null);
|
|
171
|
+
const [inputValue, setInputValue] = useState(getLocaleFormattedDate(value));
|
|
172
|
+
const [pickerValue, setPickerValue] = useState(getDateFromISO(value));
|
|
173
|
+
const [isFocused, setIsFocused] = useState(false);
|
|
174
|
+
const [pickerVisible, setPickerVisible] = useState(false);
|
|
175
|
+
useEffect(() => {
|
|
176
|
+
const updated = getLocaleFormattedDate(value);
|
|
177
|
+
if (updated) {
|
|
178
|
+
setInputValue(updated);
|
|
179
|
+
setPickerValue(getDateFromISO(value));
|
|
180
|
+
}
|
|
181
|
+
}, [value]);
|
|
182
|
+
const needsPositionTop = useCallback(() => {
|
|
183
|
+
const element = wrapperRef.current;
|
|
184
|
+
if (!element) {
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
const {
|
|
188
|
+
top,
|
|
189
|
+
height
|
|
190
|
+
} = element.getBoundingClientRect();
|
|
191
|
+
const distToBottom = document.body.offsetHeight - (top + height);
|
|
192
|
+
return distToBottom <= 300;
|
|
193
|
+
}, [wrapperRef]);
|
|
194
|
+
const handleInputValueChange = useCallback(val => {
|
|
195
|
+
setInputValue(val);
|
|
196
|
+
}, []);
|
|
197
|
+
const handleFocusChange = useCallback((focused, e) => {
|
|
198
|
+
if (!focused) {
|
|
199
|
+
setPickerValue(getDateFromISO(getISOFormattedDate(inputValue)));
|
|
200
|
+
onChange(inputValue.length !== 0 ? getISOFormattedDate(inputValue, false) : null);
|
|
201
|
+
}
|
|
202
|
+
setIsFocused(focused);
|
|
203
|
+
onFocusChange(focused, e);
|
|
204
|
+
}, [inputValue, onChange, onFocusChange]);
|
|
205
|
+
const handlePickerChange = useCallback(date => {
|
|
206
|
+
setPickerVisible(false);
|
|
207
|
+
setPickerValue(date);
|
|
208
|
+
setInputValue(getLocaleFormattedDate(date));
|
|
209
|
+
onChange(getISOFormattedDate(date));
|
|
210
|
+
}, [onChange]);
|
|
211
|
+
const handleMonthChange = useCallback(date => {
|
|
212
|
+
setPickerValue(date);
|
|
213
|
+
}, []);
|
|
214
|
+
const handleIconClick = useCallback(() => {
|
|
215
|
+
setPickerVisible(!pickerVisible);
|
|
216
|
+
}, [pickerVisible]);
|
|
217
|
+
const handleBackdropClick = useCallback(() => {
|
|
218
|
+
setPickerVisible(false);
|
|
219
|
+
}, []);
|
|
220
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
|
|
221
|
+
ref: wrapperRef
|
|
222
|
+
}, /*#__PURE__*/React.createElement(SimpleInput, _extends({}, rest, {
|
|
223
|
+
attributes: {
|
|
224
|
+
placeholder: isFocused ? i18n.text('formats.date.pattern') : null
|
|
225
|
+
},
|
|
226
|
+
value: inputValue,
|
|
227
|
+
type: "text",
|
|
228
|
+
onFocusChange: handleFocusChange,
|
|
229
|
+
onChange: handleInputValueChange
|
|
230
|
+
})), /*#__PURE__*/React.createElement("div", {
|
|
231
|
+
className: styles.iconWrapper,
|
|
232
|
+
onClick: handleIconClick,
|
|
233
|
+
role: "button",
|
|
234
|
+
tabIndex: "0",
|
|
235
|
+
onKeyDown: handleIconClick
|
|
236
|
+
}, /*#__PURE__*/React.createElement(CalendarIcon, null))), /*#__PURE__*/React.createElement("div", {
|
|
237
|
+
className: classNames(styles.pickerWrapper, {
|
|
238
|
+
[styles.hidden]: !pickerVisible
|
|
239
|
+
})
|
|
240
|
+
}, /*#__PURE__*/React.createElement(DayPicker, {
|
|
241
|
+
className: classNames('DayPickerInput-Overlay', styles.picker, {
|
|
242
|
+
[styles.pickerTop]: needsPositionTop()
|
|
243
|
+
}),
|
|
244
|
+
onDayClick: handlePickerChange,
|
|
245
|
+
selectedDays: pickerValue,
|
|
246
|
+
localeUtils: MomentLocaleUtils,
|
|
247
|
+
locale: locale,
|
|
248
|
+
todayButton: i18n.text('locations.your_current_timeslot.today'),
|
|
249
|
+
onTodayButtonClick: handlePickerChange,
|
|
250
|
+
month: pickerValue,
|
|
251
|
+
showOutsideDays: true,
|
|
252
|
+
captionElement: ({
|
|
253
|
+
date,
|
|
254
|
+
localeUtils
|
|
255
|
+
}) => /*#__PURE__*/React.createElement(Caption, {
|
|
256
|
+
date: date,
|
|
257
|
+
localeUtils: localeUtils,
|
|
258
|
+
onChange: handleMonthChange
|
|
259
|
+
})
|
|
260
|
+
})), pickerVisible && /*#__PURE__*/React.createElement(Backdrop, {
|
|
261
|
+
isVisible: true,
|
|
262
|
+
level: 11,
|
|
263
|
+
opacity: 0,
|
|
264
|
+
onClick: handleBackdropClick
|
|
265
|
+
}));
|
|
266
|
+
};
|
|
267
|
+
DateInput.defaultProps = {
|
|
268
|
+
onFocusChange: () => {},
|
|
269
|
+
onChange: () => {},
|
|
270
|
+
type: null,
|
|
271
|
+
value: null
|
|
272
|
+
};
|
|
273
|
+
export default DateInput;
|
|
@@ -1,26 +1,112 @@
|
|
|
1
|
-
|
|
1
|
+
import _extends from "@babel/runtime/helpers/extends";
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import classNames from 'classnames';
|
|
4
|
+
import { logger } from '@shopgate/pwa-core';
|
|
5
|
+
import SimpleInput from "./SimpleInput";
|
|
6
|
+
|
|
7
|
+
/**
|
|
2
8
|
* A component that takes care of rendering and validation of input fields.
|
|
3
9
|
* This component has no styling and should not be used directly.
|
|
4
10
|
* You may want to use an appropriate form field component from the template instead.
|
|
5
|
-
*/
|
|
11
|
+
*/
|
|
12
|
+
class MultiLineInput extends SimpleInput {
|
|
13
|
+
/**
|
|
6
14
|
* Creates a new input component.
|
|
7
15
|
* @param {Object} props The component properties.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
16
|
+
*/
|
|
17
|
+
constructor(props) {
|
|
18
|
+
super(props);
|
|
19
|
+
/**
|
|
20
|
+
* Sets an initial height of the multiline HTMLElement.
|
|
21
|
+
*/
|
|
22
|
+
this.setBaseHeight = () => {
|
|
23
|
+
if (this.baseHeight !== null) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
this.baseHeight = this.ref.clientHeight;
|
|
27
|
+
};
|
|
28
|
+
this.baseHeight = null;
|
|
29
|
+
// First render must be empty.
|
|
30
|
+
const sanitizedValue = this.props.onSanitize('');
|
|
31
|
+
this.state = {
|
|
32
|
+
value: sanitizedValue,
|
|
33
|
+
isValid: this.props.onValidate(sanitizedValue, true),
|
|
34
|
+
isFocused: false
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
12
39
|
* Additional function to make avoid .setState use in componentDidMount
|
|
13
40
|
* usage violation.
|
|
14
|
-
*/
|
|
41
|
+
*/
|
|
42
|
+
doComponentDidMount() {
|
|
43
|
+
const sanitizedValue = this.props.onSanitize(this.props.value || '');
|
|
44
|
+
this.props.onChange(sanitizedValue);
|
|
45
|
+
this.setState({
|
|
46
|
+
value: sanitizedValue,
|
|
47
|
+
isValid: this.props.onValidate(sanitizedValue, true),
|
|
48
|
+
isFocused: false
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
15
53
|
* Set real value and trigger second render.
|
|
16
54
|
* Initially trigger onChange() to set the initial value.
|
|
17
|
-
*/
|
|
18
|
-
|
|
55
|
+
*/
|
|
56
|
+
componentDidMount() {
|
|
57
|
+
// No super, would have to sanitize anyway.
|
|
58
|
+
this.doComponentDidMount();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
19
62
|
* Adjusts the element height.
|
|
20
|
-
*/
|
|
63
|
+
*/
|
|
64
|
+
componentDidUpdate() {
|
|
65
|
+
if (!(this.ref instanceof HTMLElement)) {
|
|
66
|
+
logger.error('Ref is not an HTMLElement');
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
this.ref.style.height = `${this.baseHeight}px`;
|
|
70
|
+
this.ref.style.height = `${this.ref.scrollHeight}px`;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
21
73
|
* Handles reference passing to callback and assignation.
|
|
22
74
|
* @param {HTMLElement} ref The element
|
|
23
|
-
*/
|
|
75
|
+
*/
|
|
76
|
+
handleRef(ref) {
|
|
77
|
+
super.handleRef(ref);
|
|
78
|
+
this.setBaseHeight();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
24
82
|
* Renders the component.
|
|
25
83
|
* @returns {JSX}
|
|
26
|
-
*/
|
|
84
|
+
*/
|
|
85
|
+
render() {
|
|
86
|
+
const {
|
|
87
|
+
className,
|
|
88
|
+
password,
|
|
89
|
+
attributes,
|
|
90
|
+
disabled,
|
|
91
|
+
required
|
|
92
|
+
} = this.props;
|
|
93
|
+
const type = password ? 'password' : this.props.type;
|
|
94
|
+
const {
|
|
95
|
+
value
|
|
96
|
+
} = this.state;
|
|
97
|
+
return /*#__PURE__*/React.createElement("textarea", _extends({
|
|
98
|
+
id: this.props.id,
|
|
99
|
+
name: this.props.name,
|
|
100
|
+
ref: ref => this.handleRef(ref),
|
|
101
|
+
className: classNames(className, 'multiLineInput', 'common__multi-line-input'),
|
|
102
|
+
type: type,
|
|
103
|
+
value: value,
|
|
104
|
+
onChange: this.handleChange,
|
|
105
|
+
onFocus: this.handleFocus,
|
|
106
|
+
onBlur: this.handleBlur,
|
|
107
|
+
required: required,
|
|
108
|
+
disabled: disabled
|
|
109
|
+
}, attributes));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
export default MultiLineInput;
|