@shopgate/pwa-common 7.30.0-alpha.7 → 7.30.0-alpha.9
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 +214 -7
- 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 +21 -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 +228 -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 -4
- 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,6 +1,45 @@
|
|
|
1
|
-
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { mount } from 'enzyme';
|
|
3
|
+
import { i18n } from '@shopgate/engage/core';
|
|
4
|
+
import I18n from "../../index";
|
|
5
|
+
import FormatNumber from "./index";
|
|
6
|
+
jest.unmock('@shopgate/engage/core/helpers/i18n');
|
|
7
|
+
describe('<FormatNumber>', () => {
|
|
8
|
+
describe('i18n not ready', () => {
|
|
9
|
+
it('should return same number and className when i18n is not ready', () => {
|
|
10
|
+
const component = mount(/*#__PURE__*/React.createElement(FormatNumber, {
|
|
11
|
+
number: 1,
|
|
12
|
+
fractions: 2,
|
|
13
|
+
className: "some-class"
|
|
14
|
+
}));
|
|
15
|
+
expect(component.html().includes('some-class')).toBe(true);
|
|
16
|
+
expect(component.text().includes('1')).toBe(true);
|
|
17
|
+
expect(component).toMatchSnapshot();
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
describe('i18n ready', () => {
|
|
21
|
+
i18n.init({
|
|
22
|
+
locales: {},
|
|
23
|
+
lang: 'en-US'
|
|
24
|
+
});
|
|
25
|
+
const pairs = [[1, '1.00', 2], [0.1, '0.10', 2], [1, '1.000', 3], [1, '1', 0]];
|
|
26
|
+
|
|
27
|
+
/**
|
|
2
28
|
* Makes a component.
|
|
3
29
|
* @param {number} number Number
|
|
4
30
|
* @param {number} fractions Decimal points.
|
|
5
31
|
* @returns {Object}
|
|
6
|
-
*/
|
|
32
|
+
*/
|
|
33
|
+
const makeComponent = (number, fractions) => mount(/*#__PURE__*/React.createElement(I18n.Provider, null, /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement(FormatNumber, {
|
|
34
|
+
number: number,
|
|
35
|
+
fractions: fractions
|
|
36
|
+
}))));
|
|
37
|
+
pairs.forEach(([input, expexted, fractions]) => {
|
|
38
|
+
it(`should format ${input} into ${expexted}`, () => {
|
|
39
|
+
const component = makeComponent(input, fractions);
|
|
40
|
+
expect(component.text().includes(`${expexted}`)).toBe(true);
|
|
41
|
+
expect(component).toMatchSnapshot();
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
});
|
|
@@ -1,5 +1,35 @@
|
|
|
1
|
-
|
|
1
|
+
import React, { memo } from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import { i18n } from '@shopgate/engage/core/helpers/i18n';
|
|
4
|
+
/**
|
|
2
5
|
* Formats a price.
|
|
3
6
|
* @param {Object} props The component props.
|
|
4
7
|
* @returns {JSX}
|
|
5
|
-
*/
|
|
8
|
+
*/
|
|
9
|
+
const FormatPrice = props => {
|
|
10
|
+
const {
|
|
11
|
+
className,
|
|
12
|
+
...formatProps
|
|
13
|
+
} = props;
|
|
14
|
+
if (!className) {
|
|
15
|
+
return FormatPrice.format(formatProps);
|
|
16
|
+
}
|
|
17
|
+
return /*#__PURE__*/React.createElement("span", {
|
|
18
|
+
className: className
|
|
19
|
+
}, FormatPrice.format(formatProps));
|
|
20
|
+
};
|
|
21
|
+
FormatPrice.format = ({
|
|
22
|
+
price,
|
|
23
|
+
currency,
|
|
24
|
+
fractions
|
|
25
|
+
}) => {
|
|
26
|
+
if (!i18n.ready) {
|
|
27
|
+
return price;
|
|
28
|
+
}
|
|
29
|
+
return i18n.price(price, currency, fractions);
|
|
30
|
+
};
|
|
31
|
+
FormatPrice.defaultProps = {
|
|
32
|
+
className: null,
|
|
33
|
+
fractions: true
|
|
34
|
+
};
|
|
35
|
+
export default /*#__PURE__*/memo(FormatPrice);
|
|
@@ -1 +1,46 @@
|
|
|
1
|
-
import React from'react';
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { mount } from 'enzyme';
|
|
3
|
+
import { i18n } from '@shopgate/engage/core';
|
|
4
|
+
import I18n from "../../index";
|
|
5
|
+
jest.unmock('@shopgate/engage/core/helpers/i18n');
|
|
6
|
+
describe('<FormatPrice />', () => {
|
|
7
|
+
const locales = {
|
|
8
|
+
greeting: 'Hello {price}'
|
|
9
|
+
};
|
|
10
|
+
const price = 1234.56;
|
|
11
|
+
const formattedPrice = '€1,234.56';
|
|
12
|
+
const currency = 'EUR';
|
|
13
|
+
const lang = 'en-US';
|
|
14
|
+
i18n.init({
|
|
15
|
+
locales,
|
|
16
|
+
lang
|
|
17
|
+
});
|
|
18
|
+
describe('Given the component was mounted to the DOM', () => {
|
|
19
|
+
let renderedElement;
|
|
20
|
+
it('should match snapshot', () => {
|
|
21
|
+
renderedElement = mount(/*#__PURE__*/React.createElement(I18n.Provider, null, /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("span", {
|
|
22
|
+
className: "only-price"
|
|
23
|
+
}, /*#__PURE__*/React.createElement(I18n.Price, {
|
|
24
|
+
price: price,
|
|
25
|
+
currency: currency
|
|
26
|
+
})), /*#__PURE__*/React.createElement("span", {
|
|
27
|
+
className: "text-with-price"
|
|
28
|
+
}, /*#__PURE__*/React.createElement(I18n.Text, {
|
|
29
|
+
string: "greeting"
|
|
30
|
+
}, /*#__PURE__*/React.createElement(I18n.Price, {
|
|
31
|
+
forKey: "price",
|
|
32
|
+
price: price,
|
|
33
|
+
currency: currency
|
|
34
|
+
}))))));
|
|
35
|
+
expect(renderedElement).toMatchSnapshot();
|
|
36
|
+
});
|
|
37
|
+
it('should render formatted price', () => {
|
|
38
|
+
const text = renderedElement.find('.only-price').text();
|
|
39
|
+
expect(text).toBe(formattedPrice);
|
|
40
|
+
});
|
|
41
|
+
it('should render within translated text', () => {
|
|
42
|
+
const text = renderedElement.find('.text-with-price').text();
|
|
43
|
+
expect(text).toBe(`Hello ${formattedPrice}`);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
});
|
|
@@ -1,5 +1,29 @@
|
|
|
1
|
-
import React,{Fragment,memo}from'react';
|
|
1
|
+
import React, { Fragment, memo } from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import { i18n } from '@shopgate/engage/core/helpers/i18n';
|
|
4
|
+
|
|
5
|
+
/**
|
|
2
6
|
* Formats a time.
|
|
3
7
|
* @param {Object} props The component props.
|
|
4
8
|
* @returns {JSX}
|
|
5
|
-
*/
|
|
9
|
+
*/
|
|
10
|
+
const FormatTime = ({
|
|
11
|
+
timestamp,
|
|
12
|
+
format
|
|
13
|
+
}) => /*#__PURE__*/React.createElement(Fragment, null, FormatTime.format({
|
|
14
|
+
timestamp,
|
|
15
|
+
format
|
|
16
|
+
}));
|
|
17
|
+
FormatTime.format = ({
|
|
18
|
+
timestamp,
|
|
19
|
+
format
|
|
20
|
+
}) => {
|
|
21
|
+
if (!i18n.ready) {
|
|
22
|
+
return timestamp;
|
|
23
|
+
}
|
|
24
|
+
return i18n.time(timestamp, format);
|
|
25
|
+
};
|
|
26
|
+
FormatTime.defaultProps = {
|
|
27
|
+
format: 'medium'
|
|
28
|
+
};
|
|
29
|
+
export default /*#__PURE__*/memo(FormatTime);
|
|
@@ -1,2 +1,43 @@
|
|
|
1
|
-
import React from'react';
|
|
2
|
-
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { mount } from 'enzyme';
|
|
3
|
+
import { i18n } from '@shopgate/engage/core';
|
|
4
|
+
import I18n from "../../index";
|
|
5
|
+
jest.unmock('@shopgate/engage/core/helpers/i18n');
|
|
6
|
+
describe('<FormatTime />', () => {
|
|
7
|
+
const locales = {
|
|
8
|
+
greeting: 'Hello {time}'
|
|
9
|
+
};
|
|
10
|
+
const timestamp = new Date('Dec 25, 1999 04:25:45').getTime();
|
|
11
|
+
const formattedTime = '4:25:45 AM';
|
|
12
|
+
const format = 'medium';
|
|
13
|
+
const lang = 'en-US';
|
|
14
|
+
i18n.init({
|
|
15
|
+
locales,
|
|
16
|
+
lang
|
|
17
|
+
});
|
|
18
|
+
describe('Given the component was mounted to the DOM', () => {
|
|
19
|
+
// TODO: Handle snapshot test.
|
|
20
|
+
const renderedElement = mount(/*#__PURE__*/React.createElement(I18n.Provider, null, /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("span", {
|
|
21
|
+
className: "only-time"
|
|
22
|
+
}, /*#__PURE__*/React.createElement(I18n.Time, {
|
|
23
|
+
timestamp: timestamp,
|
|
24
|
+
format: format
|
|
25
|
+
})), /*#__PURE__*/React.createElement("span", {
|
|
26
|
+
className: "text-with-time"
|
|
27
|
+
}, /*#__PURE__*/React.createElement(I18n.Text, {
|
|
28
|
+
string: "greeting"
|
|
29
|
+
}, /*#__PURE__*/React.createElement(I18n.Time, {
|
|
30
|
+
forKey: "time",
|
|
31
|
+
timestamp: timestamp,
|
|
32
|
+
format: format
|
|
33
|
+
}))))));
|
|
34
|
+
it('should render formatted time', () => {
|
|
35
|
+
const text = renderedElement.find('.only-time').text();
|
|
36
|
+
expect(text).toBe(formattedTime);
|
|
37
|
+
});
|
|
38
|
+
it('should render within translated text', () => {
|
|
39
|
+
const text = renderedElement.find('.text-with-time').text();
|
|
40
|
+
expect(text).toBe(`Hello ${formattedTime}`);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -1,16 +1,59 @@
|
|
|
1
|
-
|
|
1
|
+
import React, { Component } from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import { i18n } from '@shopgate/engage/core/helpers/i18n';
|
|
4
|
+
import { logger } from '@shopgate/pwa-core/helpers';
|
|
5
|
+
|
|
6
|
+
/**
|
|
2
7
|
* A React component that provides child components with i18n features.
|
|
3
8
|
* @returns {React.Component}
|
|
4
|
-
*/
|
|
9
|
+
*/
|
|
10
|
+
export default class I18nProvider extends Component {
|
|
11
|
+
/**
|
|
5
12
|
* @inheritDoc
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
13
|
+
*/
|
|
14
|
+
constructor(props) {
|
|
15
|
+
super(props);
|
|
16
|
+
// eslint-disable-next-line react/prop-types
|
|
17
|
+
/**
|
|
18
|
+
* Gets a shortcut to i18n functionality with preset language.
|
|
19
|
+
* @returns {Object}
|
|
20
|
+
*/
|
|
21
|
+
this.getI18nInstance = () => ({
|
|
22
|
+
__: i18n.text,
|
|
23
|
+
_p: i18n.price,
|
|
24
|
+
_d: i18n.date,
|
|
25
|
+
_t: i18n.time,
|
|
26
|
+
_n: i18n.number
|
|
27
|
+
});
|
|
28
|
+
if (this.props.lang || this.props.locales) {
|
|
29
|
+
logger.warn(`===== I18nProvider deprecated =====\nI18nProvider and it's related components (@shopgate/pwa-common/component/I18n) or context types are deprecated and will be removed in @shopgate/engage v7.0.0.\nPlease use { i18n } from @shopgate/engage/core.\n===================================
|
|
30
|
+
`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
11
35
|
* Provides context for child components.
|
|
12
36
|
* @returns {Object}
|
|
13
|
-
*/
|
|
37
|
+
*/
|
|
38
|
+
getChildContext() {
|
|
39
|
+
return {
|
|
40
|
+
i18n: this.getI18nInstance
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
14
44
|
* Renders the component.
|
|
15
45
|
* @returns {JSX}
|
|
16
|
-
*/
|
|
46
|
+
*/
|
|
47
|
+
render() {
|
|
48
|
+
const {
|
|
49
|
+
children
|
|
50
|
+
} = this.props;
|
|
51
|
+
return children ? /*#__PURE__*/React.cloneElement(children) : null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
I18nProvider.childContextTypes = {
|
|
55
|
+
i18n: PropTypes.func.isRequired
|
|
56
|
+
};
|
|
57
|
+
I18nProvider.defaultProps = {
|
|
58
|
+
children: null
|
|
59
|
+
};
|
|
@@ -1 +1,39 @@
|
|
|
1
|
-
import React from'react';
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { shallow } from 'enzyme';
|
|
3
|
+
import { i18n as i18nHelper } from '@shopgate/engage/core';
|
|
4
|
+
import I18nProvider from "./index";
|
|
5
|
+
jest.unmock('@shopgate/engage/core/helpers/i18n');
|
|
6
|
+
describe('<I18nProvider />', () => {
|
|
7
|
+
const locales = {
|
|
8
|
+
greeting: 'Guten Tag {name}'
|
|
9
|
+
};
|
|
10
|
+
const lang = 'de-DE';
|
|
11
|
+
i18nHelper.init({
|
|
12
|
+
locales,
|
|
13
|
+
lang
|
|
14
|
+
});
|
|
15
|
+
describe('Given the component was mounted to the DOM', () => {
|
|
16
|
+
let renderedElement;
|
|
17
|
+
let renderedInstance;
|
|
18
|
+
it('should match snapshot', () => {
|
|
19
|
+
renderedElement = shallow(/*#__PURE__*/React.createElement(I18nProvider, null));
|
|
20
|
+
renderedInstance = renderedElement.instance();
|
|
21
|
+
expect(renderedElement).toMatchSnapshot();
|
|
22
|
+
});
|
|
23
|
+
it('should provide access to i18n via context', () => {
|
|
24
|
+
const {
|
|
25
|
+
i18n
|
|
26
|
+
} = renderedInstance.getChildContext();
|
|
27
|
+
expect(i18n).toEqual(renderedInstance.getI18nInstance);
|
|
28
|
+
});
|
|
29
|
+
it('should translate when calling an instance method', () => {
|
|
30
|
+
const {
|
|
31
|
+
__
|
|
32
|
+
} = renderedInstance.getI18nInstance();
|
|
33
|
+
const translated = __('greeting', {
|
|
34
|
+
name: 'Test'
|
|
35
|
+
});
|
|
36
|
+
expect(translated).toBe('Guten Tag Test');
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
});
|
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
import React,{Fragment}from'react';
|
|
1
|
+
import React, { Fragment } from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
|
|
4
|
+
/**
|
|
2
5
|
* A placeholder for a translation key.
|
|
3
6
|
* @param {Object} props The component props.
|
|
4
7
|
* @param {Object} context The component context.
|
|
5
8
|
* @returns {JSX}
|
|
6
|
-
*/
|
|
9
|
+
*/
|
|
10
|
+
const Placeholder = props => /*#__PURE__*/React.createElement(Fragment, null, props.children);
|
|
11
|
+
Placeholder.format = props => /*#__PURE__*/React.createElement(Placeholder, props);
|
|
12
|
+
export default Placeholder;
|
|
@@ -1 +1,30 @@
|
|
|
1
|
-
import React from'react';
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { mount } from 'enzyme';
|
|
3
|
+
import { i18n } from '@shopgate/engage/core';
|
|
4
|
+
import I18n from "../../index";
|
|
5
|
+
jest.unmock('@shopgate/engage/core/helpers/i18n');
|
|
6
|
+
describe('<Placeholder />', () => {
|
|
7
|
+
const locales = {
|
|
8
|
+
greeting: 'Hello {world}'
|
|
9
|
+
};
|
|
10
|
+
const lang = 'en-US';
|
|
11
|
+
i18n.init({
|
|
12
|
+
locales,
|
|
13
|
+
lang
|
|
14
|
+
});
|
|
15
|
+
describe('Given the component was mounted to the DOM', () => {
|
|
16
|
+
let renderedElement;
|
|
17
|
+
it('should render', () => {
|
|
18
|
+
renderedElement = mount(/*#__PURE__*/React.createElement(I18n.Provider, null, /*#__PURE__*/React.createElement(I18n.Text, {
|
|
19
|
+
string: "greeting"
|
|
20
|
+
}, /*#__PURE__*/React.createElement(I18n.Placeholder, {
|
|
21
|
+
forKey: "world"
|
|
22
|
+
}, /*#__PURE__*/React.createElement("strong", null, "WORLD")), "/")));
|
|
23
|
+
expect(renderedElement).toMatchSnapshot();
|
|
24
|
+
});
|
|
25
|
+
it('should render with a placeholder text', () => {
|
|
26
|
+
const text = renderedElement.find('strong').text();
|
|
27
|
+
expect(text).toBe('WORLD');
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
});
|
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
import _extends from "@babel/runtime/helpers/extends";
|
|
2
|
+
import "core-js/modules/es.array.reduce.js";
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import PropTypes from 'prop-types';
|
|
5
|
+
import { renderToString } from 'react-dom/server';
|
|
6
|
+
import { logger } from '@shopgate/pwa-core';
|
|
7
|
+
import { i18n } from '@shopgate/engage/core/helpers/i18n';
|
|
8
|
+
|
|
9
|
+
/**
|
|
2
10
|
* Returns a translation and replaces placeholder with children output.
|
|
3
11
|
* It is possible to either pass JSX components or plain strings as replacement for
|
|
4
12
|
* the translation keys.
|
|
@@ -15,9 +23,62 @@ var _excluded=["string","children","params","className","role","transform","acce
|
|
|
15
23
|
* @param {boolean} [acceptPlainTextWithPlaceholders=false] When set to TRUE, the component can
|
|
16
24
|
* also handle human readable texts that contain text replacement placeholders.
|
|
17
25
|
* @returns {JSX.Element} The translated string as JSX component.
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
26
|
+
*/
|
|
27
|
+
const Translate = ({
|
|
28
|
+
string,
|
|
29
|
+
children,
|
|
30
|
+
params,
|
|
31
|
+
className,
|
|
32
|
+
role,
|
|
33
|
+
transform,
|
|
34
|
+
acceptPlainTextWithPlaceholders,
|
|
35
|
+
...rest
|
|
36
|
+
}) => {
|
|
37
|
+
if (typeof string !== 'string' || string.length === 0) {
|
|
38
|
+
return string;
|
|
39
|
+
}
|
|
40
|
+
if (!i18n.ready) {
|
|
41
|
+
return /*#__PURE__*/React.createElement("span", _extends({
|
|
42
|
+
className: className,
|
|
43
|
+
role: role
|
|
44
|
+
}, rest), string);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// When the input string is malformed, return the original string rather than raising an error.
|
|
48
|
+
let formatted = string;
|
|
49
|
+
try {
|
|
50
|
+
// First replace every occurrence of a translation key with a separator.
|
|
51
|
+
const separator = '__%S%__';
|
|
52
|
+
const childrenArray = React.Children.toArray(children);
|
|
53
|
+
const values = childrenArray.reduce((obj, child) => child.props && child.props.forKey ? {
|
|
54
|
+
...obj,
|
|
55
|
+
[child.props.forKey]: separator
|
|
56
|
+
} : obj, {
|
|
57
|
+
...params
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// Split the tokenized string at the separators.
|
|
61
|
+
const stringParts = i18n.text(string, values, {
|
|
62
|
+
acceptPlainTextWithPlaceholders
|
|
63
|
+
}).split(separator);
|
|
64
|
+
|
|
65
|
+
// Create a new array containing the separated chunks of the text and merge the substitutions.
|
|
66
|
+
// The result can be an array with multiple strings and will be joined together.
|
|
67
|
+
formatted = stringParts.reduce((result, text, index) => [...result, text, childrenArray[index]], []);
|
|
68
|
+
} catch (e) {
|
|
69
|
+
logger.error('i18n error for string %s', string, e);
|
|
70
|
+
}
|
|
71
|
+
return /*#__PURE__*/React.createElement("span", _extends({
|
|
72
|
+
className: className,
|
|
73
|
+
role: role
|
|
74
|
+
}, rest), !transform ? formatted : transform(renderToString(formatted)));
|
|
75
|
+
};
|
|
76
|
+
Translate.defaultProps = {
|
|
77
|
+
children: null,
|
|
78
|
+
className: null,
|
|
79
|
+
params: {},
|
|
80
|
+
role: null,
|
|
81
|
+
transform: null,
|
|
82
|
+
acceptPlainTextWithPlaceholders: false
|
|
83
|
+
};
|
|
84
|
+
export default Translate;
|
|
@@ -1 +1,30 @@
|
|
|
1
|
-
import React from'react';
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { mount } from 'enzyme';
|
|
3
|
+
import { i18n } from '@shopgate/engage/core';
|
|
4
|
+
import I18n from "../../index";
|
|
5
|
+
jest.unmock('@shopgate/engage/core/helpers/i18n');
|
|
6
|
+
describe('<Translate />', () => {
|
|
7
|
+
const locales = {
|
|
8
|
+
greeting: 'Hello {name}'
|
|
9
|
+
};
|
|
10
|
+
i18n.init({
|
|
11
|
+
locales,
|
|
12
|
+
lang: 'en-US'
|
|
13
|
+
});
|
|
14
|
+
describe('Given the component was mounted to the DOM', () => {
|
|
15
|
+
let renderedElement;
|
|
16
|
+
it('should match snapshot', () => {
|
|
17
|
+
renderedElement = mount(/*#__PURE__*/React.createElement(I18n.Provider, null, /*#__PURE__*/React.createElement(I18n.Text, {
|
|
18
|
+
string: "greeting",
|
|
19
|
+
params: {
|
|
20
|
+
name: 'Test'
|
|
21
|
+
}
|
|
22
|
+
})));
|
|
23
|
+
expect(renderedElement).toMatchSnapshot();
|
|
24
|
+
});
|
|
25
|
+
it('should render translated text', () => {
|
|
26
|
+
const text = renderedElement.find(I18n.Text).text();
|
|
27
|
+
expect(text).toBe('Hello Test');
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
});
|
package/components/I18n/index.js
CHANGED
|
@@ -1 +1,16 @@
|
|
|
1
|
-
import I18nProvider from"./components/I18nProvider";
|
|
1
|
+
import I18nProvider from "./components/I18nProvider";
|
|
2
|
+
import Placeholder from "./components/Placeholder";
|
|
3
|
+
import Translate from "./components/Translate";
|
|
4
|
+
import FormatDate from "./components/FormatDate";
|
|
5
|
+
import FormatTime from "./components/FormatTime";
|
|
6
|
+
import FormatPrice from "./components/FormatPrice";
|
|
7
|
+
import FormatNumber from "./components/FormatNumber";
|
|
8
|
+
export default {
|
|
9
|
+
Provider: I18nProvider,
|
|
10
|
+
Text: Translate,
|
|
11
|
+
Date: FormatDate,
|
|
12
|
+
Time: FormatTime,
|
|
13
|
+
Placeholder,
|
|
14
|
+
Price: FormatPrice,
|
|
15
|
+
Number: FormatNumber
|
|
16
|
+
};
|
package/components/Icon/index.js
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import React from'react';
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import styles from "./style";
|
|
4
|
+
|
|
5
|
+
/**
|
|
2
6
|
* The Icon component.
|
|
3
7
|
* @param {Object} props The component props.
|
|
4
8
|
* @param {string} props.content The SVG content of the icon
|
|
@@ -6,4 +10,23 @@ import React from'react';import PropTypes from'prop-types';import styles from"./
|
|
|
6
10
|
* @param {string} [props.viewBox] The viewBox attribute passed to the SVG
|
|
7
11
|
* @param {number} [props.size=24] The icon size
|
|
8
12
|
* @returns {JSX.Element}
|
|
9
|
-
*/
|
|
13
|
+
*/
|
|
14
|
+
const Icon = props => /*#__PURE__*/React.createElement("svg", {
|
|
15
|
+
className: `${styles} ${props.className} common__icon`,
|
|
16
|
+
viewBox: props.viewBox,
|
|
17
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
18
|
+
dangerouslySetInnerHTML: {
|
|
19
|
+
__html: props.content
|
|
20
|
+
},
|
|
21
|
+
style: {
|
|
22
|
+
fontSize: props.size,
|
|
23
|
+
fill: props.color
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
Icon.defaultProps = {
|
|
27
|
+
className: '',
|
|
28
|
+
color: null,
|
|
29
|
+
viewBox: '0 0 24 24',
|
|
30
|
+
size: 'inherit'
|
|
31
|
+
};
|
|
32
|
+
export default Icon;
|
package/components/Icon/style.js
CHANGED