@shopgate/engage 7.28.0-beta.6 → 7.29.0-alpha.2
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/category/components/CategoryImage/index.js +1 -1
- package/category/components/CategoryList/index.js +7 -3
- package/category/components/CategoryList/style.js +1 -1
- package/components/ResponsiveContainer/breakpoints.js +2 -2
- package/components/SheetList/components/Item/index.js +7 -7
- package/components/Typography/Typography.d.ts +132 -0
- package/components/Typography/Typography.js +11 -0
- package/components/Typography/index.js +1 -0
- package/components/View/components/Content/style.js +1 -1
- package/components/View/context.js +1 -1
- package/components/index.js +1 -1
- package/core/constants/index.js +7 -1
- package/core/contexts/ThemeResourcesContext.d.ts +10 -1
- package/core/contexts/ThemeResourcesContext.js +1 -1
- package/core/helpers/scrollContainer.js +2 -2
- package/core/hocs/withThemeResources.js +4 -1
- package/core/hooks/events/index.js +1 -1
- package/core/hooks/events/usePressHandler.js +38 -0
- package/core/hooks/useThemeResources.js +6 -5
- package/core/providers/ThemeResourcesProvider.js +9 -5
- package/locations/subscriptions.js +2 -2
- package/package.json +9 -7
- package/page/action-creators/index.js +22 -0
- package/page/components/Widgets/Overlay.js +51 -0
- package/page/components/Widgets/Tooltip.js +22 -0
- package/page/components/Widgets/Widget.js +17 -0
- package/page/components/Widgets/WidgetContext.d.ts +42 -0
- package/page/components/Widgets/WidgetContext.js +9 -0
- package/page/components/Widgets/WidgetProvider.js +8 -0
- package/page/components/Widgets/Widgets.js +11 -0
- package/page/components/Widgets/WidgetsPreviewContext.js +9 -0
- package/page/components/Widgets/WidgetsPreviewProvider.js +8 -0
- package/page/components/Widgets/constants.js +4 -0
- package/page/components/Widgets/events.js +23 -0
- package/page/components/Widgets/helpers.js +23 -0
- package/page/components/Widgets/hooks.js +69 -0
- package/page/components/Widgets/index.js +1 -0
- package/page/components/Widgets/types.d.ts +132 -0
- package/page/components/index.js +1 -1
- package/page/constants/actionTypes.js +1 -0
- package/page/constants/index.js +5 -1
- package/page/helpers/index.d.ts +47 -0
- package/page/helpers/index.js +12 -0
- package/page/hooks/index.d.ts +60 -0
- package/page/hooks/index.js +25 -0
- package/page/index.js +1 -2
- package/page/reducers/index.js +15 -0
- package/page/selectors/index.js +48 -2
- package/page/subscriptions/index.js +4 -0
- package/page/widgets/CategoryList/CategoryList.js +4 -0
- package/page/widgets/CategoryList/hooks.js +14 -0
- package/page/widgets/CategoryList/index.js +1 -0
- package/page/widgets/CategoryList/selectors.js +8 -0
- package/page/widgets/HTML/HTML.js +5 -0
- package/page/widgets/HTML/hooks.js +12 -0
- package/page/widgets/HTML/index.js +1 -0
- package/page/widgets/Headline/Headline.js +5 -0
- package/page/widgets/Headline/hooks.js +8 -0
- package/page/widgets/Headline/index.js +1 -0
- package/page/widgets/Placeholder/Placeholder.js +5 -0
- package/page/widgets/Placeholder/hooks.js +12 -0
- package/page/widgets/Placeholder/index.js +1 -0
- package/page/widgets/ProductList/ProductList.js +5 -0
- package/page/widgets/ProductList/hooks.js +19 -0
- package/page/widgets/ProductList/index.js +1 -0
- package/page/widgets/ProductSlider/ProductSlider.js +5 -0
- package/page/widgets/ProductSlider/hooks.js +22 -0
- package/page/widgets/ProductSlider/index.js +1 -0
- package/page/widgets/index.js +1 -0
- package/page/widgets/widgets.json +20 -0
- package/product/components/ProductCard/index.js +1 -1
- package/product/components/ProductGrid/components/Item/components/ItemDetails/index.js +8 -0
- package/product/components/ProductGrid/components/Item/components/ItemDetails/spec.js +1 -0
- package/product/components/ProductGrid/components/Item/components/ItemDiscount/index.js +5 -0
- package/product/components/ProductGrid/components/Item/components/ItemFavoritesButton/index.js +5 -0
- package/product/components/ProductGrid/components/Item/components/ItemFavoritesButton/spec.js +1 -0
- package/product/components/ProductGrid/components/Item/components/ItemImage/index.js +5 -0
- package/product/components/ProductGrid/components/Item/components/ItemImage/spec.js +1 -0
- package/product/components/ProductGrid/components/Item/components/ItemName/index.js +5 -0
- package/product/components/ProductGrid/components/Item/components/ItemName/spec.js +1 -0
- package/product/components/ProductGrid/components/Item/components/ItemPrice/index.js +5 -0
- package/product/components/ProductGrid/components/Item/components/ItemPrice/spec.js +1 -0
- package/product/components/ProductGrid/components/Item/index.js +7 -0
- package/product/components/ProductGrid/components/Iterator/index.js +5 -0
- package/product/components/ProductGrid/components/Layout/index.js +5 -0
- package/product/components/ProductGrid/index.js +22 -0
- package/product/components/ProductGrid/spec.js +1 -0
- package/product/components/ProductGridPrice/index.js +1 -1
- package/product/components/ProductSlider/index.js +4 -4
- package/product/components/index.js +1 -1
- package/styles/helpers/color.js +23 -0
- package/styles/helpers/index.js +1 -1
- package/styles/helpers/setPageBackgroundColor.js +2 -2
- package/styles/index.d.ts +17 -0
- package/styles/index.js +1 -1
- package/styles/theme/createTheme/createBreakpoints.d.ts +114 -0
- package/styles/theme/createTheme/createBreakpoints.js +41 -0
- package/styles/theme/createTheme/createPalette.d.ts +36 -0
- package/styles/theme/createTheme/createPalette.js +4 -0
- package/styles/theme/createTheme/createSpacing.d.ts +23 -0
- package/styles/theme/createTheme/createSpacing.js +14 -0
- package/styles/theme/createTheme/createTypography.d.ts +55 -0
- package/styles/theme/createTheme/createTypography.js +23 -0
- package/styles/theme/createTheme/index.d.ts +41 -0
- package/styles/theme/createTheme/index.js +5 -0
- package/styles/theme/createTheme/transitions.d.ts +100 -0
- package/styles/theme/createTheme/transitions.js +26 -0
- package/styles/theme/createTheme/zIndex.d.ts +12 -0
- package/styles/theme/createTheme/zIndex.js +3 -0
- package/styles/theme/hooks/index.d.ts +4 -0
- package/styles/theme/hooks/index.js +1 -0
- package/styles/theme/hooks/useActiveBreakpoint.d.ts +18 -0
- package/styles/theme/hooks/useActiveBreakpoint.js +4 -0
- package/styles/theme/hooks/useMediaQuery.d.ts +33 -0
- package/styles/theme/hooks/useMediaQuery.js +20 -0
- package/styles/theme/hooks/useResponsiveValue.d.ts +27 -0
- package/styles/theme/hooks/useResponsiveValue.js +4 -0
- package/styles/theme/hooks/useTheme.d.ts +8 -0
- package/styles/theme/hooks/useTheme.js +4 -0
- package/styles/theme/index.d.ts +8 -0
- package/styles/theme/index.js +1 -0
- package/styles/theme/providers/ActiveBreakpointProvider.d.ts +21 -0
- package/styles/theme/providers/ActiveBreakpointProvider.js +13 -0
- package/styles/theme/providers/ThemeProvider.d.ts +18 -0
- package/styles/theme/providers/ThemeProvider.js +7 -0
- package/styles/tss/index.js +3 -0
- package/tracking/selectors/cookieConsent.js +2 -2
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { WidgetContextType } from '../components/Widgets/WidgetContext';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The useWidget hook provides access to the context that is wrapped around a widget.
|
|
5
|
+
* @returns The widget context.
|
|
6
|
+
*/
|
|
7
|
+
export declare function useWidget<C = Record<string, any>>(): WidgetContextType<C>
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
export type UseWidgetProductsOptions = {
|
|
11
|
+
/**
|
|
12
|
+
* The search value to use for the product search
|
|
13
|
+
*/
|
|
14
|
+
value: string;
|
|
15
|
+
/**
|
|
16
|
+
* The type of product search to perform.
|
|
17
|
+
*/
|
|
18
|
+
type: 'searchTerm' | 'productIds' | 'brand' | 'category' | 'highlights';
|
|
19
|
+
/**
|
|
20
|
+
* The number of products to return per page.
|
|
21
|
+
* @default 32
|
|
22
|
+
*/
|
|
23
|
+
limit?: number;
|
|
24
|
+
/**
|
|
25
|
+
* Sort order for the products
|
|
26
|
+
* @default 'relevance'
|
|
27
|
+
*/
|
|
28
|
+
sort?: 'relevance' | 'priceAsc' | 'priceDesc' | 'nameAsc' | 'nameDesc';
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export type UseWidgetProductsResult = {
|
|
32
|
+
/**
|
|
33
|
+
* Function to fetch the next page of products.
|
|
34
|
+
*/
|
|
35
|
+
fetchNext: () => void;
|
|
36
|
+
/**
|
|
37
|
+
* Whether there are more products to fetch.
|
|
38
|
+
*/
|
|
39
|
+
hasNext: boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Whether the products are currently being fetched.
|
|
42
|
+
*/
|
|
43
|
+
isFetching: boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Number of products available in the current result set.
|
|
46
|
+
*/
|
|
47
|
+
totalResultCount: number;
|
|
48
|
+
/**
|
|
49
|
+
* Array of product results.
|
|
50
|
+
*/
|
|
51
|
+
results: Object[];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* The useWidgetProducts hook provides an easy way to retrieve products based on a search term or
|
|
56
|
+
* other criteria.
|
|
57
|
+
*/
|
|
58
|
+
export declare function useWidgetProducts(
|
|
59
|
+
options: UseWidgetProductsOptions
|
|
60
|
+
): UseWidgetProductsResult;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import _regeneratorRuntime from"@babel/runtime/regenerator";function asyncGeneratorStep(gen,resolve,reject,_next,_throw,key,arg){try{var info=gen[key](arg);var value=info.value;}catch(error){reject(error);return;}if(info.done){resolve(value);}else{Promise.resolve(value).then(_next,_throw);}}function _asyncToGenerator(fn){return function(){var self=this,args=arguments;return new Promise(function(resolve,reject){var gen=fn.apply(self,args);function _next(value){asyncGeneratorStep(gen,resolve,reject,_next,_throw,"next",value);}function _throw(err){asyncGeneratorStep(gen,resolve,reject,_next,_throw,"throw",err);}_next(undefined);});};}function _slicedToArray(arr,i){return _arrayWithHoles(arr)||_iterableToArrayLimit(arr,i)||_nonIterableRest();}function _nonIterableRest(){throw new TypeError("Invalid attempt to destructure non-iterable instance");}function _iterableToArrayLimit(arr,i){var _arr=[];var _n=true;var _d=false;var _e=undefined;try{for(var _i=arr[Symbol.iterator](),_s;!(_n=(_s=_i.next()).done);_n=true){_arr.push(_s.value);if(i&&_arr.length===i)break;}}catch(err){_d=true;_e=err;}finally{try{if(!_n&&_i["return"]!=null)_i["return"]();}finally{if(_d)throw _e;}}return _arr;}function _arrayWithHoles(arr){if(Array.isArray(arr))return arr;}function _extends(){_extends=Object.assign||function(target){for(var i=1;i<arguments.length;i++){var source=arguments[i];for(var key in source){if(Object.prototype.hasOwnProperty.call(source,key)){target[key]=source[key];}}}return target;};return _extends.apply(this,arguments);}import{useContext,useMemo,useCallback,useEffect,useState}from'react';import{useSelector,useDispatch}from'react-redux';import{WidgetContext}from'@shopgate/engage/page/components/Widgets';import{showInventoryInLists}from'@shopgate/engage/locations/helpers';import{ITEMS_PER_LOAD}from'@shopgate/engage/core/constants';import{transformDisplayOptions}from'@shopgate/engage/core/helpers';import{fetchProductsByQuery}from'@shopgate/engage/product';import{makeGetWidgetProducts}from"../selectors";/**
|
|
2
|
+
* @typedef {import('../components/Widgets/WidgetContext').WidgetContextType WidgetContextType}
|
|
3
|
+
*/ /**
|
|
4
|
+
* @typedef {import('./index').UseWidgetProductsOptions} UseWidgetProductsOptions
|
|
5
|
+
*/ /**
|
|
6
|
+
* @typedef {import('./index').UseWidgetProductsResult} UseWidgetProductsResult
|
|
7
|
+
*/ /**
|
|
8
|
+
* The useWidget hook provides access to the context that is wrapped around a widget.
|
|
9
|
+
* @returns {WidgetContextType} The widget context.
|
|
10
|
+
*/export var useWidget=function useWidget(){return useContext(WidgetContext);};var REQUEST_TYPE_MAPPING={highlights:1,searchTerm:2,brand:3,productIds:4,category:5};/**
|
|
11
|
+
* Retrieves the products for the current widget.
|
|
12
|
+
* @param {UseWidgetProductsOptions} options Hook options
|
|
13
|
+
* @returns {UseWidgetProductsResult} The products and a function to fetch more products.
|
|
14
|
+
*/export var useWidgetProducts=function useWidgetProducts(){var options=arguments.length>0&&arguments[0]!==undefined?arguments[0]:{};var type=options.type,value=options.value,_options$limit=options.limit,limit=_options$limit===void 0?ITEMS_PER_LOAD:_options$limit,_options$sort=options.sort,sort=_options$sort===void 0?'relevance':_options$sort;var dispatch=useDispatch();var _useWidget=useWidget(),_useWidget$code=_useWidget.code,code=_useWidget$code===void 0?"".concat(type,"_").concat(value,"_").concat(limit,"_").concat(sort):_useWidget$code;// ###### Products selection ######
|
|
15
|
+
var showInventoryInProductLists=useSelector(showInventoryInLists);var selectorOptions=useMemo(function(){return _extends({sort:transformDisplayOptions(sort),value:value,useDefaultRequestForProductIds:true},showInventoryInProductLists&&{useDefaultRequestForProductIds:true});},[showInventoryInProductLists,sort,value]);var getWidgetProducts=useMemo(function(){return makeGetWidgetProducts(type,selectorOptions,code);},[code,selectorOptions,type]);var widgetProducts=useSelector(getWidgetProducts);// ###### Products request ######
|
|
16
|
+
var _useState=useState(true),_useState2=_slicedToArray(_useState,2),hasNext=_useState2[0],setHasNext=_useState2[1];var _useState3=useState(0),_useState4=_slicedToArray(_useState3,2),offset=_useState4[0],setOffset=_useState4[1];var isFetching=widgetProducts.isFetching;var baseRequestOptions=useMemo(function(){return _extends({limit:limit,sort:transformDisplayOptions(sort),useDefaultRequestForProductIds:true},showInventoryInProductLists&&{useDefaultRequestForProductIds:true});},[limit,showInventoryInProductLists,sort]);/**
|
|
17
|
+
* Callback to dispatch the initial fetch request for products when the hook mounts,
|
|
18
|
+
* or when its parameters change.
|
|
19
|
+
*/var fetchInitial=useCallback(/*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee(){var initialOptions,result;return _regeneratorRuntime.wrap(function _callee$(_context){while(1)switch(_context.prev=_context.next){case 0:if(value){_context.next=3;break;}setHasNext(false);return _context.abrupt("return");case 3:// Initial request needs to start at offset 0
|
|
20
|
+
initialOptions=_extends({},baseRequestOptions,{offset:0});_context.next=6;return dispatch(fetchProductsByQuery(REQUEST_TYPE_MAPPING[type],value,initialOptions,code));case 6:result=_context.sent;// Re-initialize offset and hasNext based on the result
|
|
21
|
+
setOffset(limit);setHasNext(result.totalProductCount>limit);case 9:case"end":return _context.stop();}},_callee);})),[code,dispatch,type,value,limit,baseRequestOptions]);// Effect to trigger the initial fetch when the component mounts or the parameters change.
|
|
22
|
+
useEffect(function(){fetchInitial();},[fetchInitial]);/**
|
|
23
|
+
* Callback to fetch the next chunk of products when e.g. users interacted with a "Load More"
|
|
24
|
+
* button.
|
|
25
|
+
*/var fetchNext=useCallback(/*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee2(){var nextOptions,result;return _regeneratorRuntime.wrap(function _callee2$(_context2){while(1)switch(_context2.prev=_context2.next){case 0:if(!(!hasNext||isFetching||!value)){_context2.next=2;break;}return _context2.abrupt("return");case 2:nextOptions=_extends({},baseRequestOptions,{offset:offset});_context2.next=5;return dispatch(fetchProductsByQuery(REQUEST_TYPE_MAPPING[type],value,nextOptions,code));case 5:result=_context2.sent;setOffset(offset+limit);setHasNext(result.totalProductCount>offset+limit);case 8:case"end":return _context2.stop();}},_callee2);})),[code,dispatch,hasNext,isFetching,value,offset,limit,baseRequestOptions,type]);return{fetchNext:fetchNext,hasNext:hasNext,isFetching:isFetching,results:widgetProducts.products.slice(0,offset),totalResultCount:widgetProducts.totalProductCount};};
|
package/page/index.js
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
/** @module page */export*from"./actions";export*from"./components";export*from"./constants"
|
|
2
|
-
export*from"./selectors";
|
|
1
|
+
/** @module page */export*from"./actions";export*from"./components";export*from"./constants";export*from"./selectors";
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
function _extends(){_extends=Object.assign||function(target){for(var i=1;i<arguments.length;i++){var source=arguments[i];for(var key in source){if(Object.prototype.hasOwnProperty.call(source,key)){target[key]=source[key];}}}return target;};return _extends.apply(this,arguments);}import{produce}from'immer';import{REQUEST_PAGE_CONFIG_V2,RECEIVE_PAGE_CONFIG_V2,ERROR_PAGE_CONFIG_V2,PAGE_STATE_LIFETIME}from"../constants";var customWidgetRegex=/<!\x2D\x2DWidget((?:[\0-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])*)\x2D\x2D>/gm;/**
|
|
2
|
+
* Decodes entities from a HTML string
|
|
3
|
+
* @param {string} html HTML string to decode
|
|
4
|
+
* @returns {string}
|
|
5
|
+
*/var decodeHTMLEntities=function decodeHTMLEntities(){var html=arguments.length>0&&arguments[0]!==undefined?arguments[0]:'';var textarea=document.createElement('textarea');textarea.innerHTML=html;return textarea.value;};/**
|
|
6
|
+
* @param {Object} data The page data object
|
|
7
|
+
* @returns {Object} The sanitized page data
|
|
8
|
+
*/var transformCustomLegacyWidgets=function transformCustomLegacyWidgets(data){var _data$dropzones;if(!Array.isArray(data===null||data===void 0?void 0:(_data$dropzones=data.dropzones)===null||_data$dropzones===void 0?void 0:_data$dropzones.cmsWidgetList)){return data;}var cmsWidgetList=data.dropzones.cmsWidgetList;var transformedWidgets=cmsWidgetList.map(function(widget){var _widget$widgetConfig;if(widget.widgetConfigDefinitionCode!=='@shopgate/widgets/htmlWidget'||!(widget===null||widget===void 0?void 0:(_widget$widgetConfig=widget.widgetConfig)===null||_widget$widgetConfig===void 0?void 0:_widget$widgetConfig.html)){return widget;}var content=decodeHTMLEntities(widget.widgetConfig.html.trim());if(content.startsWith('<!--Widget')){try{var settings=customWidgetRegex.exec(content);customWidgetRegex.lastIndex=0;if(settings[0]===content){var customLegacyWidget=JSON.parse(settings[1]);// Convert legacy widget data to the new format
|
|
9
|
+
return _extends({},widget,{widgetConfig:customLegacyWidget.settings||{},widgetConfigDefinitionCode:customLegacyWidget.type||'',isCustomLegacyWidget:true});}}catch(err){// Nothing to do here
|
|
10
|
+
}}return widget;});return _extends({},data,{dropzones:_extends({},data.dropzones,{cmsWidgetList:transformedWidgets})});};var defaultState={};/**
|
|
11
|
+
* Stores state of the v2 implementation of pages.
|
|
12
|
+
* @param {Object} [state={}] The current state.
|
|
13
|
+
* @param {Object} action The action object.
|
|
14
|
+
* @returns {Object} The new state.
|
|
15
|
+
*/export function pageV2(){var state=arguments.length>0&&arguments[0]!==undefined?arguments[0]:defaultState;var action=arguments.length>1?arguments[1]:undefined;/* eslint-disable no-param-reassign */var producer=produce(function(draft){switch(action.type){case REQUEST_PAGE_CONFIG_V2:{var pageType=action.pageType,pageSlug=action.pageSlug;draft[pageType]=draft[pageType]||{};draft[pageType][pageSlug]={data:null,isFetching:true,expires:0};break;}case RECEIVE_PAGE_CONFIG_V2:{var _pageType=action.pageType,_pageSlug=action.pageSlug,data=action.data;draft[_pageType]=draft[_pageType]||{};draft[_pageType][_pageSlug]={data:transformCustomLegacyWidgets(data),isFetching:false,expires:Date.now()+PAGE_STATE_LIFETIME};break;}case ERROR_PAGE_CONFIG_V2:{var _pageType2=action.pageType,_pageSlug2=action.pageSlug;draft[_pageType2]=draft[_pageType2]||{};draft[_pageType2][_pageSlug2]={data:null,isFetching:false};break;}default:break;}});/* eslint-enable no-param-reassign */return producer(state);}
|
package/page/selectors/index.js
CHANGED
|
@@ -1,7 +1,53 @@
|
|
|
1
|
-
import{createSelector}from'reselect';import{makeGetMenu,makeGetIsFetchingMenu}from'@shopgate/engage/core/selectors';import{LEGAL_MENU}from'@shopgate/engage/core/constants';import{hasNewServices}from'@shopgate/engage/core';import{PRIVACY_PATH}from"../constants";export*from'@shopgate/pwa-common/selectors/page';/**
|
|
1
|
+
function _extends(){_extends=Object.assign||function(target){for(var i=1;i<arguments.length;i++){var source=arguments[i];for(var key in source){if(Object.prototype.hasOwnProperty.call(source,key)){target[key]=source[key];}}}return target;};return _extends.apply(this,arguments);}import{createSelector}from'reselect';import{makeGetMenu,makeGetIsFetchingMenu}from'@shopgate/engage/core/selectors';import{getFulfillmentParams,getPopulatedProductsResult,SHOPGATE_CATALOG_GET_HIGHLIGHT_PRODUCTS}from'@shopgate/engage/product';import{getProductState}from'@shopgate/engage/product/selectors/product';import{LEGAL_MENU,SORT_PRICE_ASC,SORT_PRICE_DESC}from'@shopgate/engage/core/constants';import{hasNewServices,transformDisplayOptions,generateResultHash}from'@shopgate/engage/core/helpers';import{PRIVACY_PATH}from"../constants";export*from'@shopgate/pwa-common/selectors/page';/**
|
|
2
2
|
* Creates a selector that retrieves the privacy policy link.
|
|
3
3
|
*
|
|
4
4
|
* When the new services are active, the link is extracted from the "shopgate.cms.getMenu" response.
|
|
5
5
|
* Otherwise it's a static link to the legacy privacy page.
|
|
6
6
|
* @returns {string|null}
|
|
7
|
-
*/export var makeGetPrivacyPolicyLink=function makeGetPrivacyPolicyLink(){var getMenu=makeGetMenu(LEGAL_MENU);var getIsFetchingMenu=makeGetIsFetchingMenu(LEGAL_MENU);return createSelector(getMenu,getIsFetchingMenu,function(menu,fetching){if(!hasNewServices()){return PRIVACY_PATH;}if(fetching||!menu){return null;}var entry=menu.find(function(item){return item.url.includes('privacy');})||{};return(entry===null||entry===void 0?void 0:entry.url)||null;});}
|
|
7
|
+
*/export var makeGetPrivacyPolicyLink=function makeGetPrivacyPolicyLink(){var getMenu=makeGetMenu(LEGAL_MENU);var getIsFetchingMenu=makeGetIsFetchingMenu(LEGAL_MENU);return createSelector(getMenu,getIsFetchingMenu,function(menu,fetching){if(!hasNewServices()){return PRIVACY_PATH;}if(fetching||!menu){return null;}var entry=menu.find(function(item){return item.url.includes('privacy');})||{};return(entry===null||entry===void 0?void 0:entry.url)||null;});};/**
|
|
8
|
+
* @param {Object} state The current application state.
|
|
9
|
+
* @return {Function} A selector function that retrieves the pageV2 state.
|
|
10
|
+
*/var getPageV2State=function getPageV2State(state){return state.pageV2;};/**
|
|
11
|
+
* List of available page types.
|
|
12
|
+
* @typedef {'cms' | 'category'} PageType
|
|
13
|
+
*/ /**
|
|
14
|
+
* List of available dropzone types.
|
|
15
|
+
* @typedef {'cmsWidgetList'} DropzoneType
|
|
16
|
+
*/ /**
|
|
17
|
+
* Creates a selector that retrieves page data based on the type and slug.
|
|
18
|
+
* @param {Object} params The selector params
|
|
19
|
+
* @param {PageType} [params.type] The type of the page.
|
|
20
|
+
* @param {string|null} [params.slug=null] The slug of the page (optional).
|
|
21
|
+
* @returns {Function} A selector function that retrieves the page data.
|
|
22
|
+
*/export var makeGetPage=function makeGetPage(_ref){var _ref$type=_ref.type,type=_ref$type===void 0?'cms':_ref$type,_ref$slug=_ref.slug,slug=_ref$slug===void 0?null:_ref$slug;return createSelector(getPageV2State,function(pageState){if(type&&slug){var _pageState$type;return((_pageState$type=pageState[type])===null||_pageState$type===void 0?void 0:_pageState$type[slug])||null;}return pageState[type]||null;});};/**
|
|
23
|
+
* Creates a selector that retrieves the widget list from a page based on the type, slug and
|
|
24
|
+
* and dropzone name.
|
|
25
|
+
* @param {Object} params The selector params
|
|
26
|
+
* @param {PageType} [params.type='cms'] The type of the page.
|
|
27
|
+
* @param {DropzoneType} [params.dropzone='cmsWidgetList'] The dropzone name to retrieve the widget
|
|
28
|
+
* list from.
|
|
29
|
+
* @param {string|null} [params.slug=null] The slug of the page (optional).
|
|
30
|
+
* @returns {Function} A selector function that retrieves the widget list.
|
|
31
|
+
*/export var makeGetWidgetsFromPage=function makeGetWidgetsFromPage(_ref2){var _ref2$type=_ref2.type,type=_ref2$type===void 0?'cms':_ref2$type,_ref2$slug=_ref2.slug,slug=_ref2$slug===void 0?null:_ref2$slug,_ref2$dropzone=_ref2.dropzone,dropzone=_ref2$dropzone===void 0?'cmsWidgetList':_ref2$dropzone;var getPage=makeGetPage({type:type,slug:slug});return createSelector(getPage,function(page){var _ref3,_page$data,_page$data$dropzones;if(!page){return undefined;}return(_ref3=(_page$data=page.data)===null||_page$data===void 0?void 0:(_page$data$dropzones=_page$data.dropzones)===null||_page$data$dropzones===void 0?void 0:_page$data$dropzones[dropzone])!==null&&_ref3!==void 0?_ref3:[];});};/**
|
|
32
|
+
* Creates a selector that generates a hash to select results for widget products.
|
|
33
|
+
* @param {'searchTerm' | 'productIds' | 'brand' | 'category' |'highlights'} type Type of the
|
|
34
|
+
* request to make.
|
|
35
|
+
* @param {Object} options Request options
|
|
36
|
+
* @param {string} id Unique identifier to find the result in the state.
|
|
37
|
+
* @returns {Function} A selector function that generates a hash for the widget products result.
|
|
38
|
+
*/var makeGetWidgetProductsResultHash=function makeGetWidgetProductsResultHash(type,options,id){var value=options.value,sort=options.sort,useDefaultRequestForProductIds=options.useDefaultRequestForProductIds,productIdType=options.productIdType;var transformedSort=transformDisplayOptions(sort);return createSelector(getFulfillmentParams,function(fulfillmentParams){var _hashParams;var hashParams={};switch(type){case'highlights':hashParams={id:id,pipeline:SHOPGATE_CATALOG_GET_HIGHLIGHT_PRODUCTS,sort:transformedSort};break;case'searchTerm':case'brand':hashParams=_extends({id:id,searchPhrase:value,sort:transformedSort},fulfillmentParams);break;case'productIds':hashParams=_extends({id:id,productIds:value,productIdType:productIdType},!useDefaultRequestForProductIds&&{sort:transformedSort},{},fulfillmentParams);break;case'category':hashParams=_extends({id:id,categoryId:value,sort:transformedSort},fulfillmentParams);break;default:}return generateResultHash(hashParams,!!((_hashParams=hashParams)===null||_hashParams===void 0?void 0:_hashParams.sort),false);});};/**
|
|
39
|
+
* @param {'searchTerm' | 'productIds' | 'brand' | 'category' |'highlights'} type Type of the
|
|
40
|
+
* request to make.
|
|
41
|
+
* @param {Object} options Request options
|
|
42
|
+
* @param {string} id Unique identifier to find the result in the state.
|
|
43
|
+
* @returns {Function} A selector function that retrieves the widget products result by hash.
|
|
44
|
+
*/var makeGetWidgetProductResultsByHash=function makeGetWidgetProductResultsByHash(type,options,id){var getWidgetProductResultsHash=makeGetWidgetProductsResultHash(type,options,id);return createSelector(getProductState,getWidgetProductResultsHash,function(productState,hash){return productState.resultsByHash[hash];});};/**
|
|
45
|
+
* Creates a selector that collects products for a widget.
|
|
46
|
+
* @param {'searchTerm' | 'productIds' | 'brand' | 'category' |'highlights'} type Type of the
|
|
47
|
+
* request to make.
|
|
48
|
+
* @param {Object} options Request options
|
|
49
|
+
* @param {string} id Unique identifier to find the result in the state.
|
|
50
|
+
* @returns {Function} A selector function that collects products for a widget.
|
|
51
|
+
*/export var makeGetWidgetProducts=function makeGetWidgetProducts(type,options,id){var getWidgetProductResultsHash=makeGetWidgetProductsResultHash(type,options,id);var getWidgetProductResultsByHash=makeGetWidgetProductResultsByHash(type,options,id);return createSelector(function(state){return state;},function(state,props){return props!==null&&props!==void 0?props:{};},getWidgetProductResultsHash,getWidgetProductResultsByHash,function(state,props,resultsHash,resultsByHash){var result=_extends({isFetching:(resultsByHash===null||resultsByHash===void 0?void 0:resultsByHash.isFetching)||false},getPopulatedProductsResult(state,props,resultsHash,resultsByHash));// Since the getProducts pipeline does not support sorting when a product ID list is
|
|
52
|
+
// provided, we need to sort the products manually here.
|
|
53
|
+
if(type==='productIds'){if(options.sort===SORT_PRICE_ASC){result.products=result.products.sort(function(p1,p2){return p1.price.unitPrice-p2.price.unitPrice;});}if(options.sort===SORT_PRICE_DESC){result.products=result.products.sort(function(p1,p2){return p2.price.unitPrice-p1.price.unitPrice;});}}return result;});};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import React from'react';import{CategoryList}from'@shopgate/engage/category/components';import{useCategoryListWidget}from"./hooks";/**
|
|
2
|
+
* The CategoryListWidget is used to display category lists.
|
|
3
|
+
* @returns {JSX.Element}
|
|
4
|
+
*/var CategoryListWidget=function CategoryListWidget(){var _useCategoryListWidge=useCategoryListWidget(),parentCategory=_useCategoryListWidge.parentCategory,categories=_useCategoryListWidge.categories,showImages=_useCategoryListWidge.showImages;if(!categories){return null;}return React.createElement(CategoryList,{categories:categories,parentCategory:parentCategory,showLeftSideImages:showImages});};export default CategoryListWidget;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import _camelCase from"lodash/camelCase";import{useEffect,useMemo}from'react';import{useSelector,useDispatch}from'react-redux';import{getCategory}from'@shopgate/pwa-common-commerce/category/selectors';import{fetchCategoryOrRootCategories}from'@shopgate/engage/category/actions';import{useWidget}from'@shopgate/engage/page/hooks';import{getCategoriesById}from"./selectors";/**
|
|
2
|
+
* @typedef {Object} CategoryListWidgetConfig
|
|
3
|
+
* @property {string} category The parent category ID to display categories for.
|
|
4
|
+
* @property {string} [sort] The sort order for categories
|
|
5
|
+
* @property {boolean} [showImages] Whether to display images for categories.
|
|
6
|
+
*/ /**
|
|
7
|
+
* @typedef {ReturnType< typeof import('@shopgate/engage/page/hooks')
|
|
8
|
+
* .useWidget<CategoryListWidgetConfig> >} UseWidgetReturnType
|
|
9
|
+
*/ // eslint-disable-next-line valid-jsdoc
|
|
10
|
+
/**
|
|
11
|
+
* Hook to access the Category List widget configuration and data.
|
|
12
|
+
*/export var useCategoryListWidget=function useCategoryListWidget(){/** @type {UseWidgetReturnType} */var _useWidget=useWidget(),config=_useWidget.config;var dispatch=useDispatch();var category=config.category,sort=config.sort,showImages=config.showImages;var sortCC=useMemo(function(){return _camelCase(sort);},[sort]);// Get the parent category object from the selected category
|
|
13
|
+
var parentCategory=useSelector(function(state){return category?getCategory(state,{categoryId:category}):null;});// Get category children of the selected category (pipeline handles sorting)
|
|
14
|
+
var categories=useSelector(function(state){return typeof category==='string'?getCategoriesById(state,{categoryId:category}):null;});var sortedCategories=useMemo(function(){if(!categories){return[];}if(sortCC==='relevance'){return categories;}var isAsc=sortCC==='nameAsc';return[].concat(categories).sort(function(a,b){return a.name.localeCompare(b.name,undefined,{sensitivity:'base'})*(isAsc?1:-1);});},[categories,sortCC]);useEffect(function(){dispatch(fetchCategoryOrRootCategories(category));},[category,dispatch]);return{parentCategory:parentCategory,showImages:showImages,categories:sortedCategories};};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{default}from"./CategoryList";
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import{createSelector}from'reselect';import{getCategoryChildren,getRootCategories}from'@shopgate/engage/category/selectors';/**
|
|
2
|
+
* Retrieves categories from the state.
|
|
3
|
+
* If no category id is passed, root-categories will be returned.
|
|
4
|
+
* @param {Object} state The application state.
|
|
5
|
+
* @param {Object} props The component props.
|
|
6
|
+
* @returns {Object[]} The categories collection.
|
|
7
|
+
*/export var getCategoriesById=createSelector(getCategoryChildren,getRootCategories,function(state,props){return props.categoryId;},function(childCategories,rootCategories,categoryId){// Check if we have to handle the root-category
|
|
8
|
+
if(!categoryId&&rootCategories){return rootCategories;}return childCategories;});
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import React from'react';import{HtmlSanitizer}from'@shopgate/engage/components';import{makeStyles}from'@shopgate/engage/styles';import{themeConfig}from'@shopgate/engage';import{useHtmlWidget}from"./hooks";var colors=themeConfig.colors;var useStyles=makeStyles()({root:{' h1, h2, h3, h4, h5, h6, p, ul, ol':{margin:'1rem 0'},' h1, h2, h3, h4, h5, h6':{fontWeight:600},' h1':{fontSize:'1.5rem'},' h2':{fontSize:'1.25rem'},' h3':{fontSize:'1.1rem'},' h4, h5, h6':{fontSize:'1rem'},' ol, ul':{paddingLeft:'1rem'},' ol > li':{listStyle:'decimal'},' ul > li':{listStyle:'disc'},' img':{display:'initial'},' img[style*="float: left"], img[style*="float:left"], img.pull-left':{marginRight:'1rem'},' img[style*="float: right"], img[style*="float:right"], img.pull-right':{marginLeft:'1rem'},' code, pre':{whiteSpace:'pre-wrap'},' blockquote, q':{paddingLeft:'1rem',margin:'2rem 0',borderLeft:".25rem solid ".concat(colors.shade6),fontStyle:'italic'},' > :first-child':{marginTop:0},// Clearfix for floated widget content
|
|
2
|
+
':after':{clear:'both',content:'.',display:'block',visibility:'hidden',height:0}}});/**
|
|
3
|
+
* The HtmlWidget component is used to display html code.
|
|
4
|
+
* @returns {JSX.Element}
|
|
5
|
+
*/var HtmlWidget=function HtmlWidget(){var _useStyles=useStyles(),classes=_useStyles.classes;var _useHtmlWidget=useHtmlWidget(),html=_useHtmlWidget.html;return React.createElement(HtmlSanitizer,{settings:{html:html},processStyles:true,className:classes.root},html);};export default HtmlWidget;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import{useWidget}from'@shopgate/engage/page/hooks';/**
|
|
2
|
+
* @typedef {Object} HtmlWidgetConfig
|
|
3
|
+
* @property {string} [html] - The HTML content to render.
|
|
4
|
+
* @property {string} [internalDescription] - Internal description for the widget.
|
|
5
|
+
*/ /**
|
|
6
|
+
* @typedef {Object} UseHtmlWidgetReturnType
|
|
7
|
+
* @property {string} [html] - The HTML content to render.
|
|
8
|
+
* @property {string} [internalDescription] - Internal description for the widget.
|
|
9
|
+
*/ /**
|
|
10
|
+
* Hook to access the html widget configuration.
|
|
11
|
+
* @returns {UseHtmlWidgetReturnType} the html widget configuration
|
|
12
|
+
*/export var useHtmlWidget=function useHtmlWidget(){/** @type {UseWidgetReturnType} */var _useWidget=useWidget(),config=_useWidget.config;var html=config.html;return{html:html};};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{default}from"./HTML";
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
function _extends(){_extends=Object.assign||function(target){for(var i=1;i<arguments.length;i++){var source=arguments[i];for(var key in source){if(Object.prototype.hasOwnProperty.call(source,key)){target[key]=source[key];}}}return target;};return _extends.apply(this,arguments);}import React from'react';import{makeStyles}from'@shopgate/engage/styles';import{Typography}from'@shopgate/engage/components';import{useHeadlineWidget}from"./hooks";var useStyles=makeStyles()(function(theme){return{root:{padding:theme.spacing(2)}};});/**
|
|
2
|
+
* The HeadlineWidget is used to display a headline text.
|
|
3
|
+
* @returns {JSX.Element}
|
|
4
|
+
*/var Headline=function Headline(){var _useStyles=useStyles(),cx=_useStyles.cx,css=_useStyles.css,classes=_useStyles.classes;var _useHeadlineWidget=useHeadlineWidget(),headline=_useHeadlineWidget.headline,align=_useHeadlineWidget.align,variant=_useHeadlineWidget.variant,styles=_useHeadlineWidget.styles;if(!headline)return null;return React.createElement(Typography// && increases the specificity of the styles which guarantees that defaults are overridden
|
|
5
|
+
,{className:cx(css({'&&':_extends({},styles)}),classes.root),variant:variant,align:align,gutterBottom:true},headline);};export default Headline;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
function _extends(){_extends=Object.assign||function(target){for(var i=1;i<arguments.length;i++){var source=arguments[i];for(var key in source){if(Object.prototype.hasOwnProperty.call(source,key)){target[key]=source[key];}}}return target;};return _extends.apply(this,arguments);}import{useWidget}from'@shopgate/engage/page/hooks';/**
|
|
2
|
+
* @typedef {Object} HeadlineWidgetConfig
|
|
3
|
+
* @property {Object} [headline] - The headline configuration object.
|
|
4
|
+
* @property {string} [headline.text] - The text to display as the headline.
|
|
5
|
+
*/ // eslint-disable-next-line valid-jsdoc
|
|
6
|
+
/**
|
|
7
|
+
* Custom hook to retrieve the headline widget configuration.
|
|
8
|
+
*/export var useHeadlineWidget=function useHeadlineWidget(){var _useWidget=useWidget(),config=_useWidget.config;var _config$headline=config.headline,bold=_config$headline.bold,italic=_config$headline.italic,text=_config$headline.text,underline=_config$headline.underline,textAlign=_config$headline.textAlign,typography=_config$headline.typography;var styles=_extends({},bold&&{fontWeight:'bold'},{},italic&&{fontStyle:'italic'},{},underline&&{textDecoration:'underline'});return{headline:text,variant:typography!==null&&typography!==void 0?typography:'h3',align:textAlign,styles:styles};};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{default}from"./Headline";
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import React from'react';import{makeStyles}from'@shopgate/engage/styles';import{usePlaceholderWidget}from"./hooks";var useStyles=makeStyles()(function(theme){return{root:{padding:theme.spacing(2),minHeight:200},name:{fontSize:14},pre:{background:'#f7f9fc',border:'1px solid #dbdde2',borderRadius:8,color:'#000',fontSize:10,overflowY:'auto',padding:theme.spacing(1)}};});/**
|
|
2
|
+
* The PlaceholderWidget component is used to display a placeholder for widget types that
|
|
3
|
+
* are not implemented yet.
|
|
4
|
+
* @returns {JSX.Element}
|
|
5
|
+
*/var PlaceholderWidget=function PlaceholderWidget(){var _useStyles=useStyles(),classes=_useStyles.classes;var _usePlaceholderWidget=usePlaceholderWidget(),code=_usePlaceholderWidget.code,name=_usePlaceholderWidget.name,config=_usePlaceholderWidget.config,layout=_usePlaceholderWidget.layout,visibility=_usePlaceholderWidget.visibility;return React.createElement("div",{className:classes.root},React.createElement("div",{className:classes.name},name),React.createElement("pre",{className:classes.pre},JSON.stringify({code:code,config:config,layout:layout,visibility:visibility},null,2)));};export default PlaceholderWidget;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import{useWidget}from'@shopgate/engage/page/hooks';/**
|
|
2
|
+
* @typedef {Object} PlaceholderWidgetConfig
|
|
3
|
+
* @property {string} foo Example property for the widget configuration.
|
|
4
|
+
* @property {number} bar Another example property for the widget configuration.
|
|
5
|
+
*/ /**
|
|
6
|
+
* @typedef {ReturnType< typeof import('@shopgate/engage/page/hooks')
|
|
7
|
+
* .useWidget<PlaceholderWidgetConfig> >} HookReturnType
|
|
8
|
+
*/ /**
|
|
9
|
+
* Local example hook to demonstrate how to extend the useWidget hook with a custom type for
|
|
10
|
+
* the widget configuration.
|
|
11
|
+
* @returns {HookReturnType}
|
|
12
|
+
*/export var usePlaceholderWidget=function usePlaceholderWidget(){return useWidget();};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{default}from"./Placeholder";
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import React from'react';import{ActionButton,I18n}from'@shopgate/engage/components';import{ProductGrid}from'@shopgate/engage/product/components';import{useWidgetProducts}from'@shopgate/engage/page/hooks';import{makeStyles}from'@shopgate/engage/styles';import{useProductListWidget}from"./hooks";var useStyles=makeStyles()({root:{// Prevent that the ActionButton margin messes with the layout of the sibling widgets
|
|
2
|
+
overflow:'hidden'},grid:{'&&':{marginTop:0}}});/**
|
|
3
|
+
* The ProductListWidget is used to display product lists.
|
|
4
|
+
* @returns {JSX.Element}
|
|
5
|
+
*/var ProductListWidget=function ProductListWidget(){var _useStyles=useStyles(),classes=_useStyles.classes;var _useProductListWidget=useProductListWidget(),productsSearchType=_useProductListWidget.productsSearchType,productsSearchValue=_useProductListWidget.productsSearchValue,sort=_useProductListWidget.sort,productCount=_useProductListWidget.productCount,showLoadMore=_useProductListWidget.showLoadMore,flags=_useProductListWidget.flags;var _useWidgetProducts=useWidgetProducts({type:productsSearchType,value:productsSearchValue,limit:productCount,sort:sort}),fetchNext=_useWidgetProducts.fetchNext,hasNext=_useWidgetProducts.hasNext,isFetching=_useWidgetProducts.isFetching,results=_useWidgetProducts.results;return React.createElement("div",{className:classes.root},React.createElement(ProductGrid,{products:results,flags:flags,scope:"widgets",infiniteLoad:false,className:classes.grid}),hasNext&&showLoadMore&&React.createElement(ActionButton,{loading:isFetching,onClick:fetchNext},React.createElement(I18n.Text,{string:"common.load_more"})));};export default ProductListWidget;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import _camelCase from"lodash/camelCase";function _extends(){_extends=Object.assign||function(target){for(var i=1;i<arguments.length;i++){var source=arguments[i];for(var key in source){if(Object.prototype.hasOwnProperty.call(source,key)){target[key]=source[key];}}}return target;};return _extends.apply(this,arguments);}import{useMemo}from'react';import{useWidget}from'@shopgate/engage/page/hooks';import{getProductSearchParamsFromProductsInputConfig}from'@shopgate/engage/page/helpers';/**
|
|
2
|
+
* @typedef {import('@shopgate/engage/page/helpers')
|
|
3
|
+
* .ProductsWidgetInputConfig} ProductsWidgetInputConfig
|
|
4
|
+
*/ /**
|
|
5
|
+
* @typedef {Object} ProductListWidgetConfig
|
|
6
|
+
* @property {ProductsWidgetInputConfig} products The products configuration for the widget.
|
|
7
|
+
* @property {number} productCount The number of products to display in the widget
|
|
8
|
+
* @property {"relevance" | "PriceDesc" | "PriceAsc"} sort Sort order for the products
|
|
9
|
+
* @property {boolean} loadMoreButton Whether to display a "Load more" button
|
|
10
|
+
* @property {boolean} showName Whether to display product names
|
|
11
|
+
* @property {boolean} showPrice Whether to display product prices
|
|
12
|
+
* @property {boolean} showRating Whether to display product ratings
|
|
13
|
+
*/ /**
|
|
14
|
+
* @typedef {ReturnType< typeof import('@shopgate/engage/page/hooks')
|
|
15
|
+
* .useWidget<ProductListWidgetConfig> >} UseWidgetReturnType
|
|
16
|
+
*/ // eslint-disable-next-line valid-jsdoc
|
|
17
|
+
/**
|
|
18
|
+
* Hook to access the Product List widget configuration.
|
|
19
|
+
*/export var useProductListWidget=function useProductListWidget(){/** @type {UseWidgetReturnType} */var _useWidget=useWidget(),config=_useWidget.config;var products=config.products,productCount=config.productCount,sort=config.sort,_config$loadMoreButto=config.loadMoreButton,loadMoreButton=_config$loadMoreButto===void 0?false:_config$loadMoreButto,_config$showName=config.showName,showName=_config$showName===void 0?false:_config$showName,_config$showPrice=config.showPrice,showPrice=_config$showPrice===void 0?false:_config$showPrice,_config$showRating=config.showRating,showRating=_config$showRating===void 0?false:_config$showRating;var productSearchParams=useMemo(function(){return getProductSearchParamsFromProductsInputConfig(products);},[products]);var flags=useMemo(function(){return{name:showName,price:showPrice,reviews:showRating};},[showName,showPrice,showRating]);return _extends({},productSearchParams,{sort:_camelCase(sort),productCount:productCount,showLoadMore:loadMoreButton,flags:flags});};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{default}from"./ProductList";
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
function _extends(){_extends=Object.assign||function(target){for(var i=1;i<arguments.length;i++){var source=arguments[i];for(var key in source){if(Object.prototype.hasOwnProperty.call(source,key)){target[key]=source[key];}}}return target;};return _extends.apply(this,arguments);}function _defineProperty(obj,key,value){if(key in obj){Object.defineProperty(obj,key,{value:value,enumerable:true,configurable:true,writable:true});}else{obj[key]=value;}return obj;}import React,{useMemo}from'react';import{ProductSlider}from'@shopgate/engage/product/components';import{useWidgetProducts}from'@shopgate/engage/page/hooks';import{useTheme}from'@shopgate/engage/styles';import{useProductSliderWidget}from"./hooks";/**
|
|
2
|
+
* The ProductSliderWidget is used to display a product slider.
|
|
3
|
+
* @returns {JSX.Element}
|
|
4
|
+
*/var ProductSliderWidget=function ProductSliderWidget(){var _useProductSliderWidg=useProductSliderWidget(),productsSearchType=_useProductSliderWidg.productsSearchType,productsSearchValue=_useProductSliderWidg.productsSearchValue,sort=_useProductSliderWidg.sort,productCount=_useProductSliderWidg.productCount,swiperProps=_useProductSliderWidg.swiperProps,productItemProps=_useProductSliderWidg.productItemProps,isPreview=_useProductSliderWidg.isPreview;var _useWidgetProducts=useWidgetProducts({type:productsSearchType,value:productsSearchValue,limit:productCount,sort:sort}),results=_useWidgetProducts.results;var theme=useTheme();var productIds=useMemo(function(){return results===null||results===void 0?void 0:results.map(function(result){return result.id;});},[results]);if(!productIds||!productIds.length){return null;}return React.createElement(ProductSlider,_extends({productIds:productIds,scope:"widgets",productItemProps:productItemProps,slidesPerView:2.3// Improves interaction with the slider in the CMS preview iframe
|
|
5
|
+
},isPreview?{touchStartPreventDefault:true}:{},{breakpoints:_defineProperty(_defineProperty(_defineProperty({},theme.breakpoints.values.sm,{slidesPerView:3.3}),theme.breakpoints.values.md,{slidesPerView:4.3}),theme.breakpoints.values.lg,{slidesPerView:5.3})},swiperProps));};export default ProductSliderWidget;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import _camelCase from"lodash/camelCase";function _extends(){_extends=Object.assign||function(target){for(var i=1;i<arguments.length;i++){var source=arguments[i];for(var key in source){if(Object.prototype.hasOwnProperty.call(source,key)){target[key]=source[key];}}}return target;};return _extends.apply(this,arguments);}import{useMemo}from'react';import{useWidget}from'@shopgate/engage/page/hooks';import{getProductSearchParamsFromProductsInputConfig}from'@shopgate/engage/page/helpers';/**
|
|
2
|
+
* @typedef {import('@shopgate/engage/page/helpers')
|
|
3
|
+
* .ProductsWidgetInputConfig} ProductsWidgetInputConfig
|
|
4
|
+
*/ /**
|
|
5
|
+
* @typedef {Object} ProductSliderWidgetConfig
|
|
6
|
+
* @property {ProductsWidgetInputConfig} products The products configuration for the widget.
|
|
7
|
+
* @property {number} productCount The number of products to display in the widget.
|
|
8
|
+
* @property {"relevance" | "priceDesc" | "priceAsc"} sort Sort order for the products.
|
|
9
|
+
* @property {boolean} [showName] Whether to display product names.
|
|
10
|
+
* @property {boolean} [showPrice] Whether to display product prices.
|
|
11
|
+
* @property {boolean} [showRating] Whether to display product ratings.
|
|
12
|
+
* @property {boolean} [slideAutomatic] Whether the slider should automatically slide.
|
|
13
|
+
* @property {boolean} [endlessSlider] Whether the slider should loop endlessly.
|
|
14
|
+
* @property {number} [sliderSpeed] The speed (in ms) for the slider autoplay.
|
|
15
|
+
* @property {boolean} [isPreview] Whether the widget is in preview mode.
|
|
16
|
+
*/ /**
|
|
17
|
+
* @typedef {ReturnType< typeof import('@shopgate/engage/page/hooks')
|
|
18
|
+
* .useWidget<ProductSliderWidgetConfig> >} UseWidgetReturnType
|
|
19
|
+
*/ // eslint-disable-next-line valid-jsdoc
|
|
20
|
+
/**
|
|
21
|
+
* Hook to access the Product Slider widget configuration.
|
|
22
|
+
*/export var useProductSliderWidget=function useProductSliderWidget(){/** @type {UseWidgetReturnType} */var _useWidget=useWidget(),config=_useWidget.config,isPreview=_useWidget.isPreview;var products=config.products,productCount=config.productCount,sort=config.sort,_config$showName=config.showName,showName=_config$showName===void 0?false:_config$showName,_config$showPrice=config.showPrice,showPrice=_config$showPrice===void 0?false:_config$showPrice,_config$showRating=config.showRating,showRating=_config$showRating===void 0?false:_config$showRating,_config$slideAutomati=config.slideAutomatic,slideAutomatic=_config$slideAutomati===void 0?true:_config$slideAutomati,_config$endlessSlider=config.endlessSlider,endlessSlider=_config$endlessSlider===void 0?true:_config$endlessSlider,_config$sliderSpeed=config.sliderSpeed,sliderSpeed=_config$sliderSpeed===void 0?7000:_config$sliderSpeed;var productSearchParams=useMemo(function(){return getProductSearchParamsFromProductsInputConfig(products);},[products]);var swiperProps=useMemo(function(){return{autoplay:slideAutomatic,delay:sliderSpeed,loop:endlessSlider};},[slideAutomatic,sliderSpeed,endlessSlider]);var productItemProps=useMemo(function(){return{hideName:!showName,hidePrice:!showPrice,hideRating:!showRating};},[showName,showPrice,showRating]);return _extends({},productSearchParams,{sort:_camelCase(sort),productCount:productCount,swiperProps:swiperProps,productItemProps:productItemProps,isPreview:isPreview});};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{default}from"./ProductSlider";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{default as PlaceholderWidget}from"./Placeholder";export{default as ProductListWidget}from"./ProductList";export{default as CategoryListWidget}from"./CategoryList";export{default as HtmlWidget}from"./HTML";export{default as ProductSliderWidget}from"./ProductSlider";export{default as HeadlineWidget}from"./Headline";
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"@shopgate/widgetsInternal/Placeholder": {
|
|
3
|
+
"path": "@shopgate/engage/page/widgets/Placeholder"
|
|
4
|
+
},
|
|
5
|
+
"@shopgate/widgets/productListWidget": {
|
|
6
|
+
"path": "@shopgate/engage/page/widgets/ProductList"
|
|
7
|
+
},
|
|
8
|
+
"@shopgate/widgets/categoryListWidget": {
|
|
9
|
+
"path": "@shopgate/engage/page/widgets/CategoryList"
|
|
10
|
+
},
|
|
11
|
+
"@shopgate/widgets/productSliderWidget": {
|
|
12
|
+
"path": "@shopgate/engage/page/widgets/ProductSlider"
|
|
13
|
+
},
|
|
14
|
+
"@shopgate/widgets/htmlWidget": {
|
|
15
|
+
"path": "@shopgate/engage/page/widgets/HTML"
|
|
16
|
+
},
|
|
17
|
+
"@shopgate/widgets/headlineWidget": {
|
|
18
|
+
"path": "@shopgate/engage/page/widgets/Headline"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -8,7 +8,7 @@ function _extends(){_extends=Object.assign||function(target){for(var i=1;i<argum
|
|
|
8
8
|
* @param {number} props.titleRows The max number of rows for the product title.
|
|
9
9
|
* @param {string} props.url Optional alternative url for the product link
|
|
10
10
|
* @return {JSX.Element}
|
|
11
|
-
*/function ProductCard(props){var product=props.product,hidePrice=props.hidePrice,hideRating=props.hideRating,hideName=props.hideName,titleRows=props.titleRows,url=props.url;var _useProductListType=useProductListType(),meta=_useProductListType.meta;var _getProductImageSetti=getProductImageSettings(),gridResolutions=_getProductImageSetti.ListImage;var _useWidgetSettings=useWidgetSettings('@shopgate/engage/rating'),_useWidgetSettings$sh=_useWidgetSettings.showEmptyRatingStars,showEmptyRatingStars=_useWidgetSettings$sh===void 0?false:_useWidgetSettings$sh;var showRatings=useMemo(function(){var _product$rating;if(!hideRating&&(product===null||product===void 0?void 0:(_product$rating=product.rating)===null||_product$rating===void 0?void 0:_product$rating.average)>0){return true;}if(!hideRating&&showEmptyRatingStars&&(product===null||product===void 0?void 0:product.rating)){return true;}return false;},[hideRating,product,showEmptyRatingStars]);return React.createElement(Link,{className:"engage__product-card",href:url||getProductRoute(product.id),itemProp:"item",itemScope:true,itemType:"http://schema.org/Product",tabIndex:0,state:_extends({},meta)},isBeta()&&product.featuredMedia?React.createElement(FeaturedMedia,{type:product.featuredMedia.type,url:product.featuredMedia.url,altText:product.featuredMedia.altText}):React.createElement(ProductImage,{src:product.featuredImageBaseUrl,resolutions:gridResolutions,alt:product.name,itemProp:"image"}),React.createElement(ProductBadges,{location:location,productId:product.id},!!(!hidePrice&&product.price.discount)&&React.createElement("div",{className:styles.badgeWrapper},React.createElement(SurroundPortals,{portalName:PRODUCT_ITEM_DISCOUNT,portalProps:{productId:product.id}},React.createElement(DiscountBadge,{text:"-".concat(product.price.discount,"%")})))),!(hidePrice&&hideRating)&&React.createElement("div",{className:"".concat(styles.details," engage__product-card__information")},showRatings&&React.createElement(RatingStars,{value:product.rating.average}),React.createElement(Swatches,{productId:product.id}),!hideName&&React.createElement(ProductName,{name:product.name,className:styles.title,testId:"Productname: ".concat(product.name),itemProp:"name",rows:titleRows||3}),React.createElement(MapPriceHint,{productId:product.id}),React.createElement(OrderQuantityHint,{productId:product.id}),!hidePrice&&React.createElement(SurroundPortals,{portalName:PRODUCT_ITEM_PRICE,portalProps:{productId:product.id,location:location}},React.createElement(ProductGridPrice,{product:product}))));}/**
|
|
11
|
+
*/function ProductCard(props){var product=props.product,hidePrice=props.hidePrice,hideRating=props.hideRating,hideName=props.hideName,titleRows=props.titleRows,url=props.url;var _useProductListType=useProductListType(),meta=_useProductListType.meta;var _getProductImageSetti=getProductImageSettings(),gridResolutions=_getProductImageSetti.ListImage;var _useWidgetSettings=useWidgetSettings('@shopgate/engage/rating'),_useWidgetSettings$sh=_useWidgetSettings.showEmptyRatingStars,showEmptyRatingStars=_useWidgetSettings$sh===void 0?false:_useWidgetSettings$sh;var showRatings=useMemo(function(){var _product$rating;if(!hideRating&&(product===null||product===void 0?void 0:(_product$rating=product.rating)===null||_product$rating===void 0?void 0:_product$rating.average)>0){return true;}if(!hideRating&&showEmptyRatingStars&&(product===null||product===void 0?void 0:product.rating)){return true;}return false;},[hideRating,product,showEmptyRatingStars]);return React.createElement(Link,{className:"engage__product-card",href:url||getProductRoute(product.id),itemProp:"item",itemScope:true,itemType:"http://schema.org/Product",tabIndex:0,state:_extends({},meta)},isBeta()&&product.featuredMedia?React.createElement(FeaturedMedia,{type:product.featuredMedia.type,url:product.featuredMedia.url,altText:product.featuredMedia.altText}):React.createElement(ProductImage,{src:product.featuredImageBaseUrl,resolutions:gridResolutions,alt:product.name,itemProp:"image"}),React.createElement(ProductBadges,{location:location,productId:product.id},!!(!hidePrice&&product.price.discount)&&React.createElement("div",{className:styles.badgeWrapper},React.createElement(SurroundPortals,{portalName:PRODUCT_ITEM_DISCOUNT,portalProps:{productId:product.id}},React.createElement(DiscountBadge,{text:"-".concat(product.price.discount,"%")})))),!(hidePrice&&hideRating&&hideName)&&React.createElement("div",{className:"".concat(styles.details," engage__product-card__information")},showRatings&&React.createElement(RatingStars,{value:product.rating.average}),React.createElement(Swatches,{productId:product.id}),!hideName&&React.createElement(ProductName,{name:product.name,className:styles.title,testId:"Productname: ".concat(product.name),itemProp:"name",rows:titleRows||3}),React.createElement(MapPriceHint,{productId:product.id}),React.createElement(OrderQuantityHint,{productId:product.id}),!hidePrice&&React.createElement(SurroundPortals,{portalName:PRODUCT_ITEM_PRICE,portalProps:{productId:product.id,location:location}},React.createElement(ProductGridPrice,{product:product}))));}/**
|
|
12
12
|
* After a refactoring of the Theme API ProductCard component, this component replaced a
|
|
13
13
|
* sub-component of the ProductCard.
|
|
14
14
|
* The original implementation exposed a couple of sub-components that don't exist in the new
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
function _extends(){_extends=Object.assign||function(target){for(var i=1;i<arguments.length;i++){var source=arguments[i];for(var key in source){if(Object.prototype.hasOwnProperty.call(source,key)){target[key]=source[key];}}}return target;};return _extends.apply(this,arguments);}import React,{useMemo,memo}from'react';import PropTypes from'prop-types';import{MapPriceHint,OrderQuantityHint,EffectivityDates,Swatches,AVAILABILITY_STATE_OK,AVAILABILITY_STATE_ALERT,getProductRoute}from'@shopgate/engage/product';import{hasNewServices as checkHasNewServices,i18n}from'@shopgate/engage/core/helpers';import{Availability,Link}from'@shopgate/engage/components';import{StockInfoLists}from'@shopgate/engage/locations/components';import{makeStyles}from'@shopgate/engage/styles';import ItemName from"../ItemName";import ItemPrice from"../ItemPrice";var useStyles=makeStyles()({root:{lineHeight:1.2,':not(:empty)':{padding:'12px 0 16px'}},quantityHint:{paddingTop:8}});/**
|
|
2
|
+
* The Product Grid Item Detail component.
|
|
3
|
+
* @param {Object} props The component props.
|
|
4
|
+
* @param {Object} props.product The product.
|
|
5
|
+
* @param {Object} props.display The display object.
|
|
6
|
+
* @param {Object} [props.productListTypeMeta] Optional meta object with data from the product list
|
|
7
|
+
* @returns {JSX.Element}
|
|
8
|
+
*/var ItemDetails=function ItemDetails(_ref){var product=_ref.product,display=_ref.display,productListTypeMeta=_ref.productListTypeMeta;var productId=product.id,_product$name=product.name,name=_product$name===void 0?null:_product$name,_product$stock=product.stock,stock=_product$stock===void 0?null:_product$stock;var _useStyles=useStyles(),classes=_useStyles.classes,cx=_useStyles.cx;var hasNewServices=useMemo(function(){return checkHasNewServices();},[]);if(display&&!display.name&&!display.price&&!display.reviews){return null;}return React.createElement(Link,{className:cx(classes.root,'theme__product-grid__item__item-details'),tabIndex:0,href:getProductRoute(productId),state:_extends({title:product.name},productListTypeMeta)},React.createElement(Swatches,{productId:productId}),React.createElement(ItemName,{display:display,productId:productId,name:name}),React.createElement(MapPriceHint,{productId:productId}),React.createElement(OrderQuantityHint,{productId:productId,className:classes.quantityHint}),React.createElement(EffectivityDates,{productId:productId}),hasNewServices&&React.createElement(React.Fragment,null,React.createElement(Availability,{state:!stock||stock.orderable?AVAILABILITY_STATE_OK:AVAILABILITY_STATE_ALERT,text:i18n.text('product.available.not'),showWhenAvailable:false}),React.createElement(StockInfoLists,{product:product})),React.createElement(ItemPrice,{product:product,display:display}));};ItemDetails.defaultProps={display:null,productListTypeMeta:null};export default memo(ItemDetails);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function _extends(){_extends=Object.assign||function(target){for(var i=1;i<arguments.length;i++){var source=arguments[i];for(var key in source){if(Object.prototype.hasOwnProperty.call(source,key)){target[key]=source[key];}}}return target;};return _extends.apply(this,arguments);}import React from'react';import{shallow}from'enzyme';import{hasNewServices}from'@shopgate/engage/core/helpers';import{Availability}from'@shopgate/engage/components';import{StockInfoLists}from'@shopgate/engage/locations/components';import ItemDetails from"./index";jest.mock('@shopgate/engage/product',function(){return{MapPriceHint:function MapPriceHint(){return null;},OrderQuantityHint:function OrderQuantityHint(){return null;},EffectivityDates:function EffectivityDates(){return null;},Swatches:function Swatches(){return null;},AVAILABILITY_STATE_OK:'AVAILABILITY_STATE_OK',AVAILABILITY_STATE_ALERT:'AVAILABILITY_STATE_ALERT',getProductRoute:jest.fn(function(productId){return"link-to-product/".concat(productId);})};});jest.mock('@shopgate/engage/locations/components',function(){return{StockInfoLists:function StockInfoLists(){return null;}};});jest.mock('@shopgate/engage/core/helpers',function(){return{hasNewServices:jest.fn().mockReturnValue(false),i18n:{text:function text(str){return str;}}};});jest.mock('@shopgate/engage/components');jest.mock('@shopgate/engage/core',function(){return{isIOSTheme:jest.fn().mockReturnValue(true),hasWebBridge:jest.fn().mockReturnValue(false),i18n:{text:function text(_text){return _text;}}};});jest.mock("../ItemName");jest.mock("../ItemPrice");describe('<ItemDetails />',function(){var props={product:{id:'1234',name:'Foo',price:{}}};var display={name:false,price:false,reviews:false};it('should render with minimal props',function(){var wrapper=shallow(React.createElement(ItemDetails,props));expect(wrapper).toMatchSnapshot();expect(wrapper.find(Availability).exists()).toBe(false);expect(wrapper.find(StockInfoLists).exists()).toBe(false);});it('should render additional components with new services',function(){hasNewServices.mockReturnValueOnce(true);var wrapper=shallow(React.createElement(ItemDetails,props));expect(wrapper).toMatchSnapshot();expect(wrapper.find(Availability).exists()).toBe(true);expect(wrapper.find(StockInfoLists).exists()).toBe(true);});it('should not render with display props set',function(){var wrapper=shallow(React.createElement(ItemDetails,_extends({},props,{display:display})));expect(wrapper).toBeEmptyRender();});});
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import React,{memo,useMemo}from'react';import PropTypes from'prop-types';import{SurroundPortals,DiscountBadge}from'@shopgate/engage/components';import{PRODUCT_ITEM_DISCOUNT}from'@shopgate/engage/category';import{makeStyles}from'@shopgate/engage/styles';var useStyles=makeStyles()({root:{minWidth:40}});/**
|
|
2
|
+
* The item discount component
|
|
3
|
+
* @param {Object} props The component props.
|
|
4
|
+
* @returns {JSX.Element|null}
|
|
5
|
+
*/var ItemDiscount=function ItemDiscount(_ref){var productId=_ref.productId,discount=_ref.discount;var _useStyles=useStyles(),classes=_useStyles.classes,cx=_useStyles.cx;var portalProps=useMemo(function(){return{productId:productId};},[productId]);if(!discount){return null;}return React.createElement("div",{className:cx(classes.root,'theme__product-grid__item__item-discount')},React.createElement(SurroundPortals,{portalName:PRODUCT_ITEM_DISCOUNT,portalProps:portalProps},React.createElement(DiscountBadge,{text:"-".concat(discount,"%")})));};ItemDiscount.defaultProps={discount:null};export default memo(ItemDiscount);
|
package/product/components/ProductGrid/components/Item/components/ItemFavoritesButton/index.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import React,{memo,useMemo}from'react';import PropTypes from'prop-types';import{useSelector}from'react-redux';import{SurroundPortals,FavoritesButton}from'@shopgate/engage/components';import{PRODUCT_ITEM_FAVORITES_BUTTON}from'@shopgate/engage/category';import{isRelativeProductOnList}from'@shopgate/engage/favorites';import{getLoadWishlistOnAppStartEnabled}from'@shopgate/engage/core';import{makeStyles}from'@shopgate/engage/styles';var useStyles=makeStyles()({root:{position:'absolute',top:0,right:16,left:'auto',transform:'translate3d(0, -50%, 0)'}});/**
|
|
2
|
+
* The item favorites button component.
|
|
3
|
+
* @param {Object} props The component props.
|
|
4
|
+
* @returns {JSX.Element}
|
|
5
|
+
*/var ItemFavoritesButton=function ItemFavoritesButton(_ref){var productId=_ref.productId;var _useStyles=useStyles(),classes=_useStyles.classes;var loadWishlistOnAppStartEnabled=useSelector(getLoadWishlistOnAppStartEnabled);var isOnWishlist=useSelector(function(state){return isRelativeProductOnList(state,{productId:productId});});var isFavorite=useMemo(function(){return!loadWishlistOnAppStartEnabled?false:isOnWishlist;},[isOnWishlist,loadWishlistOnAppStartEnabled]);var portalProps=useMemo(function(){return{productId:productId};},[productId]);return React.createElement(SurroundPortals,{portalName:PRODUCT_ITEM_FAVORITES_BUTTON,portalProps:portalProps},React.createElement("div",{className:classes.root,"data-test-id":"favorites"},React.createElement(FavoritesButton,{active:isFavorite,productId:productId,noShadow:true,removeWithRelatives:true})));};export default memo(ItemFavoritesButton);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import React from'react';import{shallow}from'enzyme';import{combineReducers}from'redux';import{Provider}from'react-redux';import{createMockStore}from'@shopgate/pwa-common/store';import favorites from'@shopgate/pwa-common-commerce/favorites/reducers';import ItemFavoritesButton from"./index";jest.mock('@shopgate/engage/components');var store=createMockStore(combineReducers({favorites:favorites}));describe('<ItemFavoritesButton />',function(){it('should not render when its not a favorite',function(){var wrapper=shallow(React.createElement(Provider,{store:store},React.createElement(ItemFavoritesButton,{productId:"1234"})));expect(wrapper).toMatchSnapshot();});it('should render if its a favorite',function(){var wrapper=shallow(React.createElement(Provider,{store:store},React.createElement(ItemFavoritesButton,{productId:"1234",isFavorite:true})));expect(wrapper).toMatchSnapshot();});});
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import React,{useMemo,memo}from'react';import PropTypes from'prop-types';import{SurroundPortals}from'@shopgate/engage/components';import{PRODUCT_ITEM_IMAGE}from'@shopgate/engage/category/constants';import{getProductImageSettings,ProductImage}from'@shopgate/engage/product';var _getProductImageSetti=getProductImageSettings(),gridResolutions=_getProductImageSetti.ListImage;/**
|
|
2
|
+
* The item image component.
|
|
3
|
+
* @param {Object} props The component props.
|
|
4
|
+
* @returns {JSX.Element}
|
|
5
|
+
*/var ItemImage=function ItemImage(_ref){var productId=_ref.productId,name=_ref.name,imageUrl=_ref.imageUrl;var portalProps=useMemo(function(){return{productId:productId};},[productId]);return React.createElement(SurroundPortals,{portalName:PRODUCT_ITEM_IMAGE,portalProps:portalProps},React.createElement(ProductImage,{alt:name,src:imageUrl,resolutions:gridResolutions,itemProp:"image"}));};ItemImage.defaultProps={imageUrl:null,name:null};export default memo(ItemImage);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import React from'react';import{shallow}from'enzyme';import ItemImage from"./index";jest.mock('@shopgate/engage/components',function(){return{ProductImage:function ProductImage(){return null;}};});describe('<ItemImage />',function(){it('should render',function(){var wrapper=shallow(React.createElement(ItemImage,{productId:"1234",imageUrl:"http://www.google.com",name:"FooBar"}));expect(wrapper).toMatchSnapshot();});});
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import React,{memo,useMemo}from'react';import PropTypes from'prop-types';import{PRODUCT_ITEM_NAME,PRODUCT_ITEM_NAME_BEFORE,PRODUCT_ITEM_NAME_AFTER}from'@shopgate/engage/category/constants';import{Portal}from'@shopgate/engage/components';import{ProductName}from'@shopgate/engage/product';import{makeStyles}from'@shopgate/engage/styles';var useStyles=makeStyles()({root:{fontWeight:'500',lineHeight:1.15,marginTop:1,wordBreak:['keep-all','break-word'],hyphens:'auto'}});/**
|
|
2
|
+
* The item name component.
|
|
3
|
+
* @param {Object} props The component props.
|
|
4
|
+
* @returns {JSX.Element|null}
|
|
5
|
+
*/var ItemName=function ItemName(_ref){var display=_ref.display,productId=_ref.productId,name=_ref.name;var _useStyles=useStyles(),classes=_useStyles.classes,cx=_useStyles.cx;var portalProps=useMemo(function(){return{productId:productId,display:display};},[display,productId]);if(display&&!display.name){return React.createElement(React.Fragment,null,React.createElement(Portal,{name:PRODUCT_ITEM_NAME_BEFORE,props:portalProps}),React.createElement(Portal,{name:PRODUCT_ITEM_NAME_AFTER,props:portalProps}));}return React.createElement(ProductName,{name:name,className:cx(classes.root,'theme__product-grid__item__item-name'),portalName:PRODUCT_ITEM_NAME,portalProps:portalProps,testId:"Productname: ".concat(name)});};ItemName.defaultProps={display:null,name:null};export default memo(ItemName);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import React from'react';import{shallow}from'enzyme';import ItemName from"./index";jest.mock('@shopgate/engage/product',function(){return{ProductName:function ProductName(){return null;}};});var props={productId:'1234',name:'Foo'};describe('<ItemName />',function(){it('should render with minimal props',function(){var wrapper=shallow(React.createElement(ItemName,props));expect(wrapper).toMatchSnapshot();});});
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import React,{memo,useMemo}from'react';import PropTypes from'prop-types';import{SurroundPortals}from'@shopgate/engage/components';import{PRODUCT_ITEM_PRICE}from'@shopgate/engage/category';import{ProductGridPrice}from'@shopgate/engage/product';/**
|
|
2
|
+
* The item price component.
|
|
3
|
+
* @param {Object} props The component props.
|
|
4
|
+
* @returns {JSX.Element|null}
|
|
5
|
+
*/var ItemPrice=function ItemPrice(_ref){var display=_ref.display,product=_ref.product;var productId=product.id;var portalProps=useMemo(function(){return{productId:productId,location:'productGrid'};},[productId]);if(display&&!display.price){return null;}return React.createElement(SurroundPortals,{portalName:PRODUCT_ITEM_PRICE,portalProps:portalProps},React.createElement(ProductGridPrice,{product:product}));};ItemPrice.defaultProps={display:null};export default memo(ItemPrice);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function _extends(){_extends=Object.assign||function(target){for(var i=1;i<arguments.length;i++){var source=arguments[i];for(var key in source){if(Object.prototype.hasOwnProperty.call(source,key)){target[key]=source[key];}}}return target;};return _extends.apply(this,arguments);}import React from'react';import{shallow}from'enzyme';import ItemPrice from"./index";jest.mock('@shopgate/engage/product',function(){return{ProductGridPrice:function ProductGridPrice(){return null;}};});jest.mock('@shopgate/engage/components',function(){return{Portal:function Portal(_ref){var children=_ref.children;return children;}};});jest.mock('@shopgate/engage/category',function(){return{PRODUCT_ITEM_PRICE:'PRODUCT_ITEM_PRICE',PRODUCT_ITEM_PRICE_AFTER:'PRODUCT_ITEM_PRICE_AFTER',PRODUCT_ITEM_PRICE_BEFORE:'PRODUCT_ITEM_PRICE_BEFORE'};});var props={product:{id:'1234',price:{}}};var display={price:false};describe('<ItemPrice />',function(){it('should render with minimal props',function(){var wrapper=shallow(React.createElement(ItemPrice,props));expect(wrapper).toMatchSnapshot();});it('should not render with display props set',function(){var wrapper=shallow(React.createElement(ItemPrice,_extends({},props,{display:display})));expect(wrapper).toBeEmptyRender();});});
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
function _extends(){_extends=Object.assign||function(target){for(var i=1;i<arguments.length;i++){var source=arguments[i];for(var key in source){if(Object.prototype.hasOwnProperty.call(source,key)){target[key]=source[key];}}}return target;};return _extends.apply(this,arguments);}import React,{memo}from'react';import PropTypes from'prop-types';import{isBeta}from'@shopgate/engage/core';import{getProductRoute,FeaturedMedia,ProductBadges}from'@shopgate/engage/product';import{Link}from'@shopgate/engage/components';import{useProductListType}from'@shopgate/engage/product/hooks';import{themeConfig}from'@shopgate/engage';import{makeStyles}from'@shopgate/engage/styles';import ItemImage from"./components/ItemImage";import ItemDiscount from"./components/ItemDiscount";import ItemFavoritesButton from"./components/ItemFavoritesButton";import ItemDetails from"./components/ItemDetails";var colors=themeConfig.colors;var useStyles=makeStyles()(function(theme,_ref){var display=_ref.display;return{root:{position:'relative',display:'flex',flexDirection:'column',background:colors.light,height:'100%'},itemDetails:_extends({position:'relative'},display&&!display.name&&!display.price&&!display.reviews&&{paddingBottom:30})};});/**
|
|
2
|
+
* The Product Grid Item component.
|
|
3
|
+
* @param {Object} props The component props.
|
|
4
|
+
* @param {Object} props.product The product.
|
|
5
|
+
* @param {Object} props.display The display object.
|
|
6
|
+
* @return {JSX.Element}
|
|
7
|
+
*/var Item=function Item(_ref2){var product=_ref2.product,display=_ref2.display;var _useStyles=useStyles({display:display}),classes=_useStyles.classes,cx=_useStyles.cx;var _useProductListType=useProductListType(),meta=_useProductListType.meta;return React.createElement("div",{className:cx(classes.root,'theme__product-grid__item')},React.createElement(Link,{role:"none",href:getProductRoute(product.id),state:_extends({title:product.name},meta)},isBeta()&&product.featuredMedia?React.createElement(FeaturedMedia,{type:product.featuredMedia.type,url:product.featuredMedia.url}):React.createElement(ItemImage,{productId:product.id,name:product.name,imageUrl:product.featuredImageBaseUrl})),React.createElement(ProductBadges,{location:"productGrid",productId:product.id},React.createElement(ItemDiscount,{productId:product.id,discount:product.price.discount||null})),React.createElement("div",{className:classes.itemDetails},React.createElement(ItemDetails,{product:product,display:display,productListTypeMeta:meta}),React.createElement(ItemFavoritesButton,{productId:product.id})));};Item.defaultProps={display:null};export default memo(Item);
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import React from'react';import PropTypes from'prop-types';import{Grid,SurroundPortals}from'@shopgate/engage/components';import{PRODUCT_ITEM}from'@shopgate/engage/category';import{ProductListEntryProvider}from'@shopgate/engage/product';import Item from"../Item";/**
|
|
2
|
+
* The Product Grid Iterator component.
|
|
3
|
+
* @param {Object} props The component props.
|
|
4
|
+
* @returns {JSX}
|
|
5
|
+
*/var Iterator=function Iterator(props){var portalProps={productId:props.id};var id=props.id,display=props.display;return React.createElement(Grid.Item,{key:id,"data-test-id":props.name,className:"theme__product-grid__item__container"},React.createElement(ProductListEntryProvider,{productId:props.id},React.createElement(SurroundPortals,{portalName:PRODUCT_ITEM,portalProps:portalProps},React.createElement(Item,{product:props,display:display}))));};Iterator.defaultProps={display:null,name:null};export default Iterator;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import React from'react';import PropTypes from'prop-types';import{Grid}from'@shopgate/engage/components';import{makeStyles}from'@shopgate/engage/styles';var useStyles=makeStyles()(function(theme,_ref){var columns=_ref.columns;return{root:{padding:'0 16px',':not(:empty)':{marginTop:16},display:'grid',gridGap:'0 16px',gridTemplateColumns:"repeat(".concat(columns,", 1fr)")}};});/**
|
|
2
|
+
* The product grid layout component.
|
|
3
|
+
* @param {Object} props The component props.
|
|
4
|
+
* @returns {JSX}
|
|
5
|
+
*/var Layout=function Layout(_ref2){var children=_ref2.children,columns=_ref2.columns,className=_ref2.className;var _useStyles=useStyles({columns:columns}),classes=_useStyles.classes,cx=_useStyles.cx;return React.createElement(Grid,{wrap:true,className:cx(classes.root,className,'theme__product-grid'),"data-test-id":"productGrid"},children);};Layout.defaultProps={className:null,children:null};export default Layout;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
function _extends(){_extends=Object.assign||function(target){for(var i=1;i<arguments.length;i++){var source=arguments[i];for(var key in source){if(Object.prototype.hasOwnProperty.call(source,key)){target[key]=source[key];}}}return target;};return _extends.apply(this,arguments);}import React,{useContext}from'react';import PropTypes from'prop-types';import{ViewContext,InfiniteContainer,LoadingIndicator}from'@shopgate/engage/components';import{ProductListTypeProvider}from'@shopgate/engage/product';import{ITEMS_PER_LOAD}from'@shopgate/engage/core/constants';import{useResponsiveValue}from'@shopgate/engage/styles';import Iterator from"./components/Iterator";import Layout from"./components/Layout";export var WIDGET_ID='@shopgate/engage/product/ProductGrid';/**
|
|
2
|
+
* The Product Grid component.
|
|
3
|
+
* @param {Object} props The component props.
|
|
4
|
+
* @param {Array} props.products The list of products to display.
|
|
5
|
+
* @param {string} props.scope Optional scope of the component. Will be used as subType property of
|
|
6
|
+
* the ProductListTypeContext and is intended as a description in which "context" the component is
|
|
7
|
+
* used.
|
|
8
|
+
* @param {Object} props.meta Optional metadata for the product list type context.
|
|
9
|
+
* @param {Object} props.flags The flags object.
|
|
10
|
+
* @param {boolean} props.flags.name Whether to display product names.
|
|
11
|
+
* @param {boolean} props.flags.price Whether to display product prices.
|
|
12
|
+
* @param {boolean} props.flags.reviews Whether to display rating stars.
|
|
13
|
+
* @param {boolean} props.infiniteLoad Whether the grid should support infinite loading.
|
|
14
|
+
* @param {Function} props.handleGetProducts Callback function that's invoked to load more products
|
|
15
|
+
* when infinite loading is enabled.
|
|
16
|
+
* @param {number} props.totalProductCount The total number of products. Needed when infinite
|
|
17
|
+
* loading is enabled.
|
|
18
|
+
* @param {string} props.requestHash The hash for the current request. Needed when infinite loading
|
|
19
|
+
* is enabled
|
|
20
|
+
* @param {string} props.className Optional class name for the grid container
|
|
21
|
+
* @returns {JSX.Element}
|
|
22
|
+
*/var ProductGrid=function ProductGrid(_ref){var flags=_ref.flags,infiniteLoad=_ref.infiniteLoad,handleGetProducts=_ref.handleGetProducts,products=_ref.products,totalProductCount=_ref.totalProductCount,requestHash=_ref.requestHash,scope=_ref.scope,meta=_ref.meta,className=_ref.className;var _useContext=useContext(ViewContext),getContentRef=_useContext.getContentRef;var columns=useResponsiveValue({xs:2,md:4});if(!infiniteLoad){return React.createElement(Layout,{columns:columns,className:className},React.createElement(ProductListTypeProvider,{type:"productGrid",subType:scope,meta:meta},products.map(function(product){return React.createElement(Iterator,_extends({display:flags,id:product.id,key:product.id},product));})));}return React.createElement(ProductListTypeProvider,{type:"productGrid",subType:scope,meta:meta},React.createElement(InfiniteContainer,{containerRef:getContentRef(),wrapper:function wrapper(props){return React.createElement(Layout,_extends({columns:columns,className:className},props));},iterator:Iterator,loader:handleGetProducts,items:products,columns:columns,loadingIndicator:React.createElement(LoadingIndicator,null),totalItems:totalProductCount,initialLimit:ITEMS_PER_LOAD,limit:ITEMS_PER_LOAD,requestHash:requestHash,enablePromiseBasedLoading:true}));};ProductGrid.defaultProps={flags:null,handleGetProducts:function handleGetProducts(){},infiniteLoad:true,products:null,requestHash:null,totalProductCount:null,scope:null,meta:null,className:null};export default ProductGrid;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import React from'react';import{mount}from'enzyme';import ProductGrid from'.';global.console.error=jest.fn();jest.mock('@shopgate/engage/core',function(){return{hasWebBridge:jest.fn(function(){return false;}),isIOSTheme:jest.fn(function(){return false;}),withForwardedRef:jest.fn(),withCurrentProduct:jest.fn(),useWidgetSettings:jest.fn().mockReturnValue({})};});jest.mock('@shopgate/engage/components',function(){var _jest$requireActual=jest.requireActual('@shopgate/engage/components/View/context'),ViewContext=_jest$requireActual.ViewContext;return{ViewContext:ViewContext,InfiniteContainer:function InfiniteContainer(){return null;},Grid:function Grid(){return null;}};});jest.mock("./components/Iterator",function(){return function Iterator(){return null;};});jest.mock('@shopgate/engage/product',function(){return{ProductListTypeProvider:function ProductListTypeProvider(_ref){var children=_ref.children;return children;},ProductListEntryProvider:function ProductListEntryProvider(_ref2){var children=_ref2.children;return children;}};});describe('<ProductGrid />',function(){it('should render with the InfiniteContainer',function(){var wrapper=mount(React.createElement(ProductGrid,{products:[]}));expect(wrapper).toMatchSnapshot();expect(wrapper.find('InfiniteContainer').exists()).toBe(true);expect(wrapper.find('Layout').exists()).toBe(false);});it('should render the original layout',function(){var wrapper=mount(React.createElement(ProductGrid,{infiniteLoad:false,products:[]}));expect(wrapper).toMatchSnapshot();expect(wrapper.find('InfiniteContainer').exists()).toBe(false);expect(wrapper.find('Layout').exists()).toBe(true);});});
|