@shopgate/engage 7.32.0-beta.5 → 7.32.0-beta.7

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.
Files changed (196) hide show
  1. package/account/components/Profile/ProfileAddressCard.d.ts.map +1 -1
  2. package/account/components/Profile/ProfileAddressCard.js +1 -0
  3. package/admin-preview/components/FrontendSettingsPreviewBridge/types.d.ts +2 -2
  4. package/admin-preview/components/FrontendSettingsPreviewBridge/types.d.ts.map +1 -1
  5. package/back-in-stock/components/Subscriptions/components/Subscription/index.js +2 -5
  6. package/cart/components/CartItem/CartItemProductLayout.js +1 -1
  7. package/cart/components/CartItem/CartItemProductLayoutWide.js +2 -2
  8. package/cart/components/CartItems/CartItems.js +8 -10
  9. package/cart/components/CartItems/CartItemsSubstitution.js +3 -2
  10. package/category/components/CategoryImage/index.js +11 -15
  11. package/components/Card/Card.d.ts +27 -0
  12. package/components/Card/Card.d.ts.map +1 -0
  13. package/components/Card/Card.js +57 -0
  14. package/components/Card/index.d.ts +4 -0
  15. package/components/Card/index.d.ts.map +1 -0
  16. package/components/Card/index.js +2 -0
  17. package/components/Card/spec.d.ts +2 -0
  18. package/components/Card/spec.d.ts.map +1 -0
  19. package/components/Card/spec.js +128 -0
  20. package/components/Card/useCardAppearance.d.ts +11 -0
  21. package/components/Card/useCardAppearance.d.ts.map +1 -0
  22. package/components/Card/useCardAppearance.js +22 -0
  23. package/components/Paper/Paper.d.ts +49 -0
  24. package/components/Paper/Paper.d.ts.map +1 -0
  25. package/components/Paper/Paper.js +64 -0
  26. package/components/Paper/index.d.ts +3 -0
  27. package/components/Paper/index.d.ts.map +1 -0
  28. package/components/Paper/index.js +1 -0
  29. package/components/Paper/spec.d.ts +2 -0
  30. package/components/Paper/spec.d.ts.map +1 -0
  31. package/components/Paper/spec.js +131 -0
  32. package/components/RadioCard/RadioCard.d.ts.map +1 -1
  33. package/components/RadioCard/RadioCard.js +2 -1
  34. package/components/constants.d.ts +6 -0
  35. package/components/constants.d.ts.map +1 -0
  36. package/components/index.d.ts +4 -1
  37. package/components/index.d.ts.map +1 -1
  38. package/components/index.js +3 -2
  39. package/core/helpers/getFullImageSource.d.ts +5 -1
  40. package/core/helpers/getFullImageSource.d.ts.map +1 -1
  41. package/core/helpers/getFullImageSource.js +30 -14
  42. package/core/helpers/i18n.d.ts +4 -0
  43. package/core/helpers/i18n.d.ts.map +1 -1
  44. package/core/helpers/i18n.js +3 -0
  45. package/core/hooks/html/useLoadImage.d.ts +3 -3
  46. package/core/hooks/html/useLoadImage.d.ts.map +1 -1
  47. package/core/hooks/html/useLoadImage.js +2 -2
  48. package/favorites/components/Item/Item.d.ts +1 -1
  49. package/favorites/components/Item/Item.d.ts.map +1 -1
  50. package/favorites/components/Item/Item.js +2 -5
  51. package/favorites/components/List/List.d.ts +1 -1
  52. package/locations/components/StoreList/StoreListProduct.js +2 -3
  53. package/orders/components/OrderHistory/OrderHistoryList.d.ts.map +1 -1
  54. package/orders/components/OrderHistory/OrderHistoryList.js +8 -9
  55. package/orders/components/OrderHistory/OrderHistoryTable.d.ts.map +1 -1
  56. package/orders/components/OrderHistory/OrderHistoryTable.js +4 -0
  57. package/package.json +8 -8
  58. package/page/widgets/ProductSlider/ProductSlider.js +1 -15
  59. package/product/components/Media/FeaturedMedia.js +7 -1
  60. package/product/components/Media/MediaImage.js +24 -23
  61. package/product/components/Media/MediaPlaceholder.js +4 -7
  62. package/product/components/MediaSlider/components/MediaImage/index.js +6 -2
  63. package/product/components/MediaSlider/constants.js +1 -12
  64. package/product/components/ProductCard/index.js +29 -23
  65. package/product/components/ProductGrid/components/Item/components/ItemDetails/index.js +6 -3
  66. package/product/components/ProductGrid/components/Item/components/ItemImage/index.js +2 -5
  67. package/product/components/ProductGrid/components/Item/components/ItemName/index.js +5 -1
  68. package/product/components/ProductGrid/components/Item/components/ItemName/spec.js +19 -1
  69. package/product/components/ProductGrid/components/Item/index.js +9 -1
  70. package/product/components/ProductGrid/hooks.d.ts +0 -7
  71. package/product/components/ProductGrid/hooks.d.ts.map +1 -1
  72. package/product/components/ProductGrid/hooks.js +9 -10
  73. package/product/components/ProductImage/ProductImage.d.ts +42 -0
  74. package/product/components/ProductImage/ProductImage.d.ts.map +1 -0
  75. package/product/components/ProductImage/ProductImage.js +153 -0
  76. package/product/components/ProductImage/ProductImagePlaceholder.d.ts +23 -0
  77. package/product/components/ProductImage/ProductImagePlaceholder.d.ts.map +1 -0
  78. package/product/components/ProductImage/ProductImagePlaceholder.js +9 -14
  79. package/product/components/ProductImage/constants.d.ts +7 -0
  80. package/product/components/ProductImage/constants.d.ts.map +1 -0
  81. package/product/components/ProductImage/constants.js +6 -0
  82. package/product/components/ProductImage/hooks.d.ts +8 -0
  83. package/product/components/ProductImage/hooks.d.ts.map +1 -0
  84. package/product/components/ProductImage/hooks.js +32 -0
  85. package/product/components/ProductImage/index.d.ts +3 -0
  86. package/product/components/ProductImage/index.d.ts.map +1 -0
  87. package/product/components/ProductImage/index.js +1 -201
  88. package/product/components/ProductImage/spec.js +115 -60
  89. package/product/components/ProductList/components/Item/index.js +5 -3
  90. package/product/components/ProductRatingStars/ProductRatingStars.js +2 -4
  91. package/product/components/ProductSlider/constants.d.ts +3 -0
  92. package/product/components/ProductSlider/constants.d.ts.map +1 -0
  93. package/product/components/ProductSlider/constants.js +2 -0
  94. package/product/components/ProductSlider/hooks.d.ts +7 -0
  95. package/product/components/ProductSlider/hooks.d.ts.map +1 -0
  96. package/product/components/ProductSlider/hooks.js +40 -0
  97. package/product/components/ProductSlider/index.js +8 -8
  98. package/product/components/ProductSlider/spec.js +2 -2
  99. package/product/components/Rating/index.js +2 -4
  100. package/product/components/RelationsSlider/RelationsSliderContent.js +3 -2
  101. package/product/components/RelationsSlider/constants.d.ts +3 -0
  102. package/product/components/RelationsSlider/constants.d.ts.map +1 -0
  103. package/product/components/RelationsSlider/constants.js +2 -1
  104. package/product/components/RelationsSlider/hooks.d.ts +6 -0
  105. package/product/components/RelationsSlider/hooks.d.ts.map +1 -0
  106. package/product/components/RelationsSlider/hooks.js +34 -0
  107. package/product/helpers/index.js +29 -11
  108. package/product/hooks/index.js +3 -1
  109. package/product/hooks/useLoadProductImage.js +4 -2
  110. package/product/hooks/useShowEmptyRatingStars.d.ts +7 -0
  111. package/product/hooks/useShowEmptyRatingStars.d.ts.map +1 -0
  112. package/product/hooks/useShowEmptyRatingStars.js +21 -0
  113. package/product/index.js +0 -1
  114. package/settings/action-creators/appSettings.d.ts +3 -3
  115. package/settings/action-creators/appSettings.d.ts.map +1 -1
  116. package/settings/constants/appSettings.d.ts +3 -0
  117. package/settings/constants/appSettings.d.ts.map +1 -1
  118. package/settings/constants/appSettings.js +6 -1
  119. package/settings/constants/imageSettings.d.ts +52 -0
  120. package/settings/constants/imageSettings.d.ts.map +1 -0
  121. package/settings/constants/imageSettings.js +69 -0
  122. package/settings/helpers/index.d.ts +3 -0
  123. package/settings/helpers/index.d.ts.map +1 -0
  124. package/settings/helpers/index.js +1 -0
  125. package/settings/helpers/resolveImageSettings.d.ts +70 -0
  126. package/settings/helpers/resolveImageSettings.d.ts.map +1 -0
  127. package/settings/helpers/resolveImageSettings.js +171 -0
  128. package/settings/helpers/toImageQuality.d.ts +7 -0
  129. package/settings/helpers/toImageQuality.d.ts.map +1 -0
  130. package/settings/helpers/toImageQuality.js +44 -0
  131. package/settings/helpers/toThumborColor.d.ts +15 -0
  132. package/settings/helpers/toThumborColor.d.ts.map +1 -0
  133. package/settings/helpers/toThumborColor.js +51 -0
  134. package/settings/hooks/index.d.ts +2 -0
  135. package/settings/hooks/index.d.ts.map +1 -0
  136. package/settings/hooks/index.js +1 -0
  137. package/settings/hooks/useImageSettings.d.ts +28 -0
  138. package/settings/hooks/useImageSettings.d.ts.map +1 -0
  139. package/settings/hooks/useImageSettings.js +39 -0
  140. package/settings/reducers/appSettings.d.ts.map +1 -1
  141. package/settings/reducers/appSettings.js +79 -11
  142. package/settings/reducers/index.d.ts +3 -3
  143. package/settings/selectors/appSettings.d.ts +71 -1
  144. package/settings/selectors/appSettings.d.ts.map +1 -1
  145. package/settings/selectors/appSettings.js +41 -1
  146. package/settings/types/appSettings.d.ts +192 -9
  147. package/settings/types/appSettings.d.ts.map +1 -1
  148. package/styles/index.d.ts +1 -1
  149. package/styles/index.d.ts.map +1 -1
  150. package/styles/reset/root.js +23 -4
  151. package/styles/reset/typographyCustomProps.d.ts +12 -0
  152. package/styles/reset/typographyCustomProps.d.ts.map +1 -0
  153. package/styles/reset/typographyCustomProps.js +11 -0
  154. package/styles/theme/createTheme/createComponents.types.d.ts +50 -0
  155. package/styles/theme/createTheme/createComponents.types.d.ts.map +1 -1
  156. package/styles/theme/createTheme/createComponents.types.js +50 -0
  157. package/styles/theme/createTheme/createCssVarsForColorSchemeThemes.d.ts.map +1 -1
  158. package/styles/theme/createTheme/createCssVarsForColorSchemeThemes.js +5 -1
  159. package/styles/theme/createTheme/createPalette.types.d.ts +4 -0
  160. package/styles/theme/createTheme/createPalette.types.d.ts.map +1 -1
  161. package/styles/theme/createTheme/createPalette.types.js +5 -1
  162. package/styles/theme/createTheme/createShape.d.ts +4 -0
  163. package/styles/theme/createTheme/createShape.d.ts.map +1 -1
  164. package/styles/theme/createTheme/createShape.js +2 -1
  165. package/styles/theme/createTheme/createThemeFromColorScheme.d.ts.map +1 -1
  166. package/styles/theme/createTheme/createThemeFromColorScheme.js +2 -1
  167. package/styles/theme/createTheme/createTypography.js +1 -0
  168. package/styles/theme/createTheme/helpers/cssVarsParser.d.ts.map +1 -1
  169. package/styles/theme/createTheme/helpers/cssVarsParser.js +10 -3
  170. package/styles/theme/createTheme/shadows.d.ts +11 -1
  171. package/styles/theme/createTheme/shadows.d.ts.map +1 -1
  172. package/styles/theme/createTheme/shadows.js +41 -7
  173. package/styles/theme/createTheme/types.d.ts +5 -1
  174. package/styles/theme/createTheme/types.d.ts.map +1 -1
  175. package/styles/theme/index.d.ts +1 -0
  176. package/styles/theme/index.d.ts.map +1 -1
  177. package/product/collections/index.js +0 -1
  178. package/product/components/EffectivityDates/constants.d.ts +0 -3
  179. package/product/components/EffectivityDates/constants.d.ts.map +0 -1
  180. package/product/components/EffectivityDates/helpers.d.ts +0 -6
  181. package/product/components/EffectivityDates/helpers.d.ts.map +0 -1
  182. package/product/components/Media/helpers.d.ts +0 -2
  183. package/product/components/Media/helpers.d.ts.map +0 -1
  184. package/product/components/ProductImage/connector.js +0 -12
  185. package/product/helpers/index.d.ts +0 -13
  186. package/product/helpers/index.d.ts.map +0 -1
  187. package/product/helpers/redirects.d.ts +0 -2
  188. package/product/helpers/redirects.d.ts.map +0 -1
  189. package/product/selectors/helpers/filterProperties.d.ts +0 -8
  190. package/product/selectors/helpers/filterProperties.d.ts.map +0 -1
  191. package/product/selectors/helpers/index.d.ts +0 -3
  192. package/product/selectors/helpers/index.d.ts.map +0 -1
  193. package/product/selectors/helpers/wrapMemoizedSelector.d.ts +0 -2
  194. package/product/selectors/helpers/wrapMemoizedSelector.d.ts.map +0 -1
  195. package/product/selectors/product.d.ts +0 -112
  196. package/product/selectors/product.d.ts.map +0 -1
@@ -1,7 +1,6 @@
1
1
  import React, { useMemo } from 'react';
2
2
  import { ProductSlider } from '@shopgate/engage/product/components';
3
3
  import { useWidgetProducts } from '@shopgate/engage/page/hooks';
4
- import { useTheme } from '@shopgate/engage/styles';
5
4
  import { useProductSliderWidget } from "./hooks";
6
5
  import WidgetHeadline from "../../components/WidgetHeadline";
7
6
 
@@ -30,7 +29,6 @@ const ProductSliderWidget = () => {
30
29
  limit: productCount,
31
30
  sort
32
31
  });
33
- const theme = useTheme();
34
32
  const productIds = useMemo(() => results?.map(result => result.id), [results]);
35
33
  if (!productIds || !productIds.length) {
36
34
  return null;
@@ -41,24 +39,12 @@ const ProductSliderWidget = () => {
41
39
  }) : null, /*#__PURE__*/_jsx(ProductSlider, {
42
40
  productIds: productIds,
43
41
  scope: "widgets",
44
- productItemProps: productItemProps,
45
- slidesPerView: 2.3
42
+ productItemProps: productItemProps
46
43
  // Improves interaction with the slider in the CMS preview iframe
47
44
  ,
48
45
  ...(isPreview ? {
49
46
  touchStartPreventDefault: true
50
47
  } : {}),
51
- breakpoints: {
52
- [theme.breakpoints.values.sm]: {
53
- slidesPerView: 3.3
54
- },
55
- [theme.breakpoints.values.md]: {
56
- slidesPerView: 4.3
57
- },
58
- [theme.breakpoints.values.lg]: {
59
- slidesPerView: 5.3
60
- }
61
- },
62
48
  ...swiperProps
63
49
  })]
64
50
  });
@@ -1,6 +1,7 @@
1
1
  import React from 'react';
2
2
  import { SurroundPortals } from '@shopgate/engage/components';
3
3
  import { MEDIA_TYPE_IMAGE, MEDIA_TYPE_VIDEO } from '@shopgate/engage/product';
4
+ import { useProductImageSettings } from '@shopgate/engage/settings/hooks';
4
5
  import { PORTAL_PRODUCT_MEDIA } from "../../../components/constants";
5
6
  import MediaImage from "./MediaImage";
6
7
  import FeaturedVideo from "./FeaturedVideo";
@@ -21,11 +22,16 @@ const FeaturedMedia = ({
21
22
  altText
22
23
  }) => {
23
24
  const TypeRenderer = types[type] || MediaImage;
25
+ const {
26
+ list
27
+ } = useProductImageSettings();
24
28
  return /*#__PURE__*/_jsx(SurroundPortals, {
25
29
  portalName: PORTAL_PRODUCT_MEDIA,
26
30
  children: /*#__PURE__*/_jsx(TypeRenderer, {
27
31
  url: url,
28
- altText: altText
32
+ altText: altText,
33
+ resolutions: list.resolutions,
34
+ ratio: list.ratio
29
35
  })
30
36
  });
31
37
  };
@@ -1,14 +1,18 @@
1
- import React, { useState, useEffect } from 'react';
1
+ import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import Image from '@shopgate/pwa-common/components/Image';
4
- import appConfig from '@shopgate/pwa-common/helpers/config';
5
4
  import { makeStyles } from '@shopgate/engage/styles';
6
5
  import { SurroundPortals } from "../../../components";
7
6
  import { PORTAL_PRODUCT_IMAGE } from "../../../components/constants";
8
- import { useWidgetSettings } from "../../../core";
7
+ import { useProductImageShadow } from "../ProductImage/hooks";
9
8
  import { defaultProps, propTypes } from "./props";
10
9
  import MediaPlaceholder from "./MediaPlaceholder";
10
+
11
+ /**
12
+ * The widget id the legacy inner shadow setting is configured under for this component.
13
+ */
11
14
  import { jsx as _jsx } from "react/jsx-runtime";
15
+ const WIDGET_ID = '@shopgate/engage/product/MediaImage';
12
16
  const useStyles = makeStyles()({
13
17
  innerShadow: {
14
18
  ':after': {
@@ -33,41 +37,37 @@ const MediaImage = ({
33
37
  url,
34
38
  altText,
35
39
  className,
36
- resolutions
40
+ resolutions,
41
+ ratio
37
42
  }) => {
38
43
  const {
39
44
  classes,
40
45
  cx
41
46
  } = useStyles();
42
- const [placeholder, setPlaceholderEnabled] = useState(!url);
43
- const {
44
- showInnerShadow = !appConfig.hideProductImageShadow
45
- } = useWidgetSettings('@shopgate/engage/product/MediaImage');
46
- useEffect(() => setPlaceholderEnabled(!url), [url]);
47
+ const showInnerShadow = useProductImageShadow(WIDGET_ID);
47
48
  const mergedClassName = cx(className, {
48
49
  [classes.innerShadow]: showInnerShadow
49
50
  });
50
- if (placeholder) {
51
- return /*#__PURE__*/_jsx(SurroundPortals, {
52
- portalName: PORTAL_PRODUCT_IMAGE,
53
- children: /*#__PURE__*/_jsx(MediaPlaceholder, {
54
- className: mergedClassName
55
- })
56
- });
57
- }
58
51
  return /*#__PURE__*/_jsx(SurroundPortals, {
59
- portalName: PORTAL_PRODUCT_IMAGE,
60
- portalProps: {
52
+ portalName: PORTAL_PRODUCT_IMAGE
53
+ // Nothing is passed on without a url, so portals keep seeing what they saw before the
54
+ // placeholder moved into the Image component.
55
+ ,
56
+ portalProps: url ? {
61
57
  src: url,
62
- resolutions
63
- },
58
+ resolutions,
59
+ ratio
60
+ } : undefined,
64
61
  children: /*#__PURE__*/_jsx(Image, {
65
62
  src: url,
66
63
  resolutions: resolutions,
64
+ ratio: ratio,
67
65
  alt: altText,
68
66
  className: mergedClassName,
69
- backgroundColor: "transparent",
70
- onError: () => setPlaceholderEnabled(true),
67
+ backgroundColor: "transparent"
68
+ // Without the merged class name - it is already on the container this renders into.
69
+ ,
70
+ placeholder: /*#__PURE__*/_jsx(MediaPlaceholder, {}),
71
71
  animating: true
72
72
  })
73
73
  });
@@ -76,6 +76,7 @@ MediaImage.defaultProps = {
76
76
  url: defaultProps.url,
77
77
  altText: defaultProps.altText,
78
78
  className: defaultProps.className,
79
+ ratio: null,
79
80
  resolutions: undefined
80
81
  };
81
82
  export default MediaImage;
@@ -5,15 +5,12 @@ import { defaultProps, propTypes } from "./props";
5
5
  import { jsx as _jsx } from "react/jsx-runtime";
6
6
  const placeholderIconScale = 0.65;
7
7
  const useStyles = makeStyles()(theme => ({
8
+ // Fills the box its parent reserves rather than reserving one of its own.
8
9
  placeholderContainer: {
9
- position: 'relative',
10
+ position: 'absolute',
10
11
  width: '100%',
11
- ':before': {
12
- display: 'block',
13
- content: '""',
14
- width: '100%',
15
- paddingTop: '100%'
16
- }
12
+ height: '100%',
13
+ top: 0
17
14
  },
18
15
  placeholderContent: {
19
16
  position: 'absolute',
@@ -1,8 +1,8 @@
1
1
  import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import { makeStyles } from '@shopgate/engage/styles';
4
+ import { useProductImageSettings } from '@shopgate/engage/settings/hooks';
4
5
  import { MediaImage as Image } from "../../../Media";
5
- import { PRODUCT_SLIDER_IMAGE_FORMATS } from "../../constants";
6
6
  import { jsx as _jsx } from "react/jsx-runtime";
7
7
  const useStyles = makeStyles()({
8
8
  full: {
@@ -23,6 +23,9 @@ const MediaImage = ({
23
23
  const {
24
24
  classes
25
25
  } = useStyles();
26
+ const {
27
+ pdp
28
+ } = useProductImageSettings();
26
29
  return /*#__PURE__*/_jsx("div", {
27
30
  onClick: onClick,
28
31
  onKeyDown: onClick,
@@ -32,7 +35,8 @@ const MediaImage = ({
32
35
  children: /*#__PURE__*/_jsx(Image, {
33
36
  url: media.url,
34
37
  altText: media.altText,
35
- resolutions: PRODUCT_SLIDER_IMAGE_FORMATS
38
+ resolutions: pdp.resolutions,
39
+ ratio: pdp.ratio
36
40
  })
37
41
  });
38
42
  };
@@ -1,15 +1,4 @@
1
1
  /**
2
2
  * key in the product image collection
3
3
  */
4
- export const PRODUCT_SLIDER_IMAGE_COLLECTION_KEY = 'PRODUCT_SLIDER_IMAGES';
5
-
6
- /**
7
- * themes value for the product slider image formats
8
- */
9
- export const PRODUCT_SLIDER_IMAGE_FORMATS = [{
10
- width: 440,
11
- height: 440
12
- }, {
13
- width: 1024,
14
- height: 1024
15
- }];
4
+ export const PRODUCT_SLIDER_IMAGE_COLLECTION_KEY = 'PRODUCT_SLIDER_IMAGES';
@@ -1,19 +1,25 @@
1
1
  import React, { useMemo } from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import { isBeta } from '@shopgate/engage/core/helpers';
4
- import { useWidgetSettings } from '@shopgate/engage/core/hooks';
5
- import { useProductListType } from '@shopgate/engage/product/hooks';
4
+ import { useProductListType, useShowEmptyRatingStars } from '@shopgate/engage/product/hooks';
6
5
  import { Link, RatingStars, DiscountBadge, SurroundPortals } from '@shopgate/engage/components';
7
6
  import { getProductRoute } from '@shopgate/engage/product';
8
7
  import { MapPriceHint, ProductImage, OrderQuantityHint, FeaturedMedia, Swatches, ProductName, ProductBadges } from '@shopgate/engage/product/components';
9
- import { getProductImageSettings } from '@shopgate/engage/product/helpers';
10
8
  import { PRODUCT_ITEM_DISCOUNT, PRODUCT_ITEM_PRICE } from '@shopgate/engage/category';
9
+ import { useSelector } from 'react-redux';
11
10
  import { makeStyles } from '@shopgate/engage/styles';
11
+ import { getProductCardNameMaxLines } from '@shopgate/engage/settings/selectors/appSettings';
12
12
  import ProductGridPrice from "../ProductGridPrice";
13
13
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
14
14
  const useStyles = makeStyles()(theme => ({
15
+ root: {
16
+ padding: theme.components.productCard.padding
17
+ },
18
+ image: {
19
+ padding: theme.components.productCard.imagePadding
20
+ },
15
21
  details: {
16
- padding: '12px 16px'
22
+ padding: theme.components.productCard.textPadding
17
23
  },
18
24
  title: {
19
25
  fontSize: theme.typography.body2.fontSize,
@@ -34,7 +40,8 @@ const location = 'productCard';
34
40
  * @param {boolean} props.hidePrice Whether the price should be hidden.
35
41
  * @param {boolean} props.hideRating Whether the rating should be hidden.
36
42
  * @param {boolean} props.hideName Whether the name should be hidden.
37
- * @param {number} props.titleRows The max number of rows for the product title.
43
+ * @param {number} props.titleRows Optional override for the max number of rows for the product
44
+ * title
38
45
  * @param {string} props.url Optional alternative url for the product link
39
46
  * @return {JSX.Element}
40
47
  */
@@ -54,12 +61,8 @@ function ProductCard(props) {
54
61
  const {
55
62
  meta
56
63
  } = useProductListType();
57
- const {
58
- ListImage: gridResolutions
59
- } = getProductImageSettings();
60
- const {
61
- showEmptyRatingStars = false
62
- } = useWidgetSettings('@shopgate/engage/rating');
64
+ const productNameLines = useSelector(getProductCardNameMaxLines);
65
+ const showEmptyRatingStars = useShowEmptyRatingStars();
63
66
  const showRatings = useMemo(() => {
64
67
  if (!hideRating && product?.rating?.average > 0) {
65
68
  return true;
@@ -70,7 +73,7 @@ function ProductCard(props) {
70
73
  return false;
71
74
  }, [hideRating, product, showEmptyRatingStars]);
72
75
  return /*#__PURE__*/_jsxs(Link, {
73
- className: "engage__product-card",
76
+ className: cx(classes.root, 'engage__product-card'),
74
77
  href: url || getProductRoute(product.id),
75
78
  itemProp: "item",
76
79
  itemScope: true,
@@ -79,15 +82,18 @@ function ProductCard(props) {
79
82
  state: {
80
83
  ...meta
81
84
  },
82
- children: [isBeta() && product.featuredMedia ? /*#__PURE__*/_jsx(FeaturedMedia, {
83
- type: product.featuredMedia.type,
84
- url: product.featuredMedia.url,
85
- altText: product.featuredMedia.altText
86
- }) : /*#__PURE__*/_jsx(ProductImage, {
87
- src: product.featuredImageBaseUrl,
88
- resolutions: gridResolutions,
89
- alt: product.name,
90
- itemProp: "image"
85
+ children: [/*#__PURE__*/_jsx("div", {
86
+ className: classes.image,
87
+ children: isBeta() && product.featuredMedia ? /*#__PURE__*/_jsx(FeaturedMedia, {
88
+ type: product.featuredMedia.type,
89
+ url: product.featuredMedia.url,
90
+ altText: product.featuredMedia.altText
91
+ }) : /*#__PURE__*/_jsx(ProductImage, {
92
+ src: product.featuredImageBaseUrl,
93
+ context: "list",
94
+ alt: product.name,
95
+ itemProp: "image"
96
+ })
91
97
  }), /*#__PURE__*/_jsx(ProductBadges, {
92
98
  location: location,
93
99
  productId: product.id,
@@ -114,7 +120,7 @@ function ProductCard(props) {
114
120
  className: classes.title,
115
121
  testId: `Productname: ${product.name}`,
116
122
  itemProp: "name",
117
- rows: titleRows || 3
123
+ rows: titleRows || productNameLines
118
124
  }), /*#__PURE__*/_jsx(MapPriceHint, {
119
125
  productId: product.id
120
126
  }), /*#__PURE__*/_jsx(OrderQuantityHint, {
@@ -150,7 +156,7 @@ ProductCard.defaultProps = {
150
156
  hideName: false,
151
157
  hidePrice: false,
152
158
  hideRating: false,
153
- titleRows: 3,
159
+ titleRows: null,
154
160
  url: null
155
161
  };
156
162
  export default ProductCard;
@@ -8,17 +8,20 @@ import { makeStyles } from '@shopgate/engage/styles';
8
8
  import ItemName from "../ItemName";
9
9
  import ItemPrice from "../ItemPrice";
10
10
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
11
- const useStyles = makeStyles()({
11
+ const useStyles = makeStyles()(theme => ({
12
12
  root: {
13
13
  lineHeight: 1.2,
14
14
  ':not(:empty)': {
15
- padding: '16px 8px 8px'
15
+ padding: theme.components.tiles.textPadding,
16
+ // Floored at the favorites button overhang so a small configured inner padding
17
+ // can't let the button overlap the product name.
18
+ paddingTop: theme.components.tiles.textPaddingTop
16
19
  }
17
20
  },
18
21
  quantityHint: {
19
22
  paddingTop: 8
20
23
  }
21
- });
24
+ }));
22
25
 
23
26
  /**
24
27
  * The Product Grid Item Detail component.
@@ -2,12 +2,9 @@ import React, { useMemo, memo } from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import { SurroundPortals } from '@shopgate/engage/components';
4
4
  import { PRODUCT_ITEM_IMAGE } from '@shopgate/engage/category/constants';
5
- import { getProductImageSettings, ProductImage } from '@shopgate/engage/product';
5
+ import { ProductImage } from '@shopgate/engage/product';
6
6
  import { makeStyles } from '@shopgate/engage/styles';
7
7
  import { jsx as _jsx } from "react/jsx-runtime";
8
- const {
9
- ListImage: gridResolutions
10
- } = getProductImageSettings();
11
8
  const useStyles = makeStyles()(theme => ({
12
9
  image: {
13
10
  background: theme.palette.background.surface
@@ -37,7 +34,7 @@ const ItemImage = ({
37
34
  className: classes.image,
38
35
  alt: name,
39
36
  src: imageUrl,
40
- resolutions: gridResolutions,
37
+ context: "list",
41
38
  itemProp: "image"
42
39
  })
43
40
  });
@@ -1,9 +1,11 @@
1
1
  import React, { memo, useMemo } from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import { PRODUCT_ITEM_NAME, PRODUCT_ITEM_NAME_BEFORE, PRODUCT_ITEM_NAME_AFTER } from '@shopgate/engage/category/constants';
4
+ import { useSelector } from 'react-redux';
4
5
  import { Portal } from '@shopgate/engage/components';
5
6
  import { ProductName, ProductRatingStars } from '@shopgate/engage/product';
6
7
  import { makeStyles } from '@shopgate/engage/styles';
8
+ import { getProductTileNameMaxLines } from '@shopgate/engage/settings/selectors/appSettings';
7
9
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
8
10
  const useStyles = makeStyles()(theme => ({
9
11
  root: {
@@ -29,6 +31,7 @@ const ItemName = ({
29
31
  classes,
30
32
  cx
31
33
  } = useStyles();
34
+ const productNameLines = useSelector(getProductTileNameMaxLines);
32
35
  const portalProps = useMemo(() => ({
33
36
  productId,
34
37
  display
@@ -54,7 +57,8 @@ const ItemName = ({
54
57
  className: cx(classes.root, 'theme__product-grid__item__item-name'),
55
58
  portalName: PRODUCT_ITEM_NAME,
56
59
  portalProps: portalProps,
57
- testId: `Productname: ${name}`
60
+ testId: `Productname: ${name}`,
61
+ rows: productNameLines
58
62
  })]
59
63
  });
60
64
  };
@@ -1,9 +1,16 @@
1
1
  import React from 'react';
2
2
  import { render } from '@testing-library/react';
3
+ import { ProductName } from '@shopgate/engage/product';
3
4
  import ItemName from "./index";
5
+
6
+ // Resolve selectors against an empty state so they fall back to the built-in appSettings defaults.
4
7
  import { jsx as _jsx } from "react/jsx-runtime";
8
+ jest.mock('react-redux', () => ({
9
+ ...jest.requireActual('react-redux'),
10
+ useSelector: selector => selector(undefined)
11
+ }));
5
12
  jest.mock('@shopgate/engage/product', () => ({
6
- ProductName: () => null,
13
+ ProductName: jest.fn(() => null),
7
14
  ProductRatingStars: () => null
8
15
  }));
9
16
  jest.mock('@shopgate/engage/components');
@@ -12,10 +19,21 @@ const props = {
12
19
  name: 'Foo'
13
20
  };
14
21
  describe('<ItemName />', () => {
22
+ beforeEach(() => {
23
+ jest.clearAllMocks();
24
+ });
15
25
  it('should render with minimal props', () => {
16
26
  const wrapper = render(/*#__PURE__*/_jsx(ItemName, {
17
27
  ...props
18
28
  }));
19
29
  expect(wrapper.asFragment()).toMatchSnapshot();
20
30
  });
31
+ it('should clamp the name to the configured tile line count', () => {
32
+ render(/*#__PURE__*/_jsx(ItemName, {
33
+ ...props
34
+ }));
35
+
36
+ // The tile name line count comes from the appSettings channel; 3 is the built-in default.
37
+ expect(ProductName.mock.calls[0][0].rows).toBe(3);
38
+ });
21
39
  });
@@ -17,7 +17,14 @@ const useStyles = makeStyles()((theme, {
17
17
  position: 'relative',
18
18
  display: 'flex',
19
19
  flexDirection: 'column',
20
- height: '100%'
20
+ height: '100%',
21
+ overflow: 'hidden',
22
+ padding: theme.components.tiles.padding,
23
+ background: theme.components.tiles.backgroundColor
24
+ },
25
+ image: {
26
+ display: 'block',
27
+ padding: theme.components.tiles.imagePadding
21
28
  },
22
29
  itemDetails: {
23
30
  position: 'relative',
@@ -50,6 +57,7 @@ const Item = ({
50
57
  return /*#__PURE__*/_jsxs("div", {
51
58
  className: cx(classes.root, 'theme__product-grid__item'),
52
59
  children: [/*#__PURE__*/_jsx(Link, {
60
+ className: classes.image,
53
61
  role: "none",
54
62
  href: getProductRoute(product.id),
55
63
  state: {
@@ -5,13 +5,6 @@
5
5
  * Precedence:
6
6
  * - Once the app settings are hydrated from a source (admin sync / jsonp), they
7
7
  * are authoritative everywhere.
8
- * - Before hydration, when the grid is rendered outside a page-builder widget
9
- * (Category / Search / legacy Products widget), the single legacy
10
- * `useWidgetSettings` scalar is honored flat across all breakpoints. This
11
- * reproduces the old theme grid and preserves the merchant's configured value.
12
- * - Otherwise (inside a widget, pre-hydration) the built-in responsive default
13
- * from the app settings is used ({ xs: 2, md: 4 }), so the inherited legacy
14
- * `columns: 2` default no longer pins tablet to 2.
15
8
  * @returns The resolved number of columns for the active breakpoint.
16
9
  */
17
10
  export declare const useProductGridColumns: () => number;
@@ -1 +1 @@
1
- {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../../../product/components/ProductGrid/hooks.ts"],"names":[],"mappings":"AAYA;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,qBAAqB,QAAO,MAwBxC,CAAC"}
1
+ {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../../../product/components/ProductGrid/hooks.ts"],"names":[],"mappings":"AAiBA;;;;;;;;GAQG;AACH,eAAO,MAAM,qBAAqB,QAAO,MAmCxC,CAAC"}
@@ -1,9 +1,11 @@
1
+ import "core-js/modules/es.array.reduce.js";
1
2
  import { useContext, useMemo } from 'react';
2
3
  import { useSelector } from 'react-redux';
3
4
  import { WidgetContext } from '@shopgate/engage/page/components/Widgets/WidgetContext';
4
5
  import { useWidgetSettings } from '@shopgate/engage/core/hooks';
5
6
  import { useResponsiveValue } from '@shopgate/engage/styles';
6
7
  import { getAreAppSettingsHydrated, getProductGridColumns } from '@shopgate/engage/settings/selectors/appSettings';
8
+ import { SCREEN_SIZE_BREAKPOINTS } from '@shopgate/engage/settings/constants/appSettings';
7
9
  import { WIDGET_ID } from "./constants";
8
10
 
9
11
  /**
@@ -13,13 +15,6 @@ import { WIDGET_ID } from "./constants";
13
15
  * Precedence:
14
16
  * - Once the app settings are hydrated from a source (admin sync / jsonp), they
15
17
  * are authoritative everywhere.
16
- * - Before hydration, when the grid is rendered outside a page-builder widget
17
- * (Category / Search / legacy Products widget), the single legacy
18
- * `useWidgetSettings` scalar is honored flat across all breakpoints. This
19
- * reproduces the old theme grid and preserves the merchant's configured value.
20
- * - Otherwise (inside a widget, pre-hydration) the built-in responsive default
21
- * from the app settings is used ({ xs: 2, md: 4 }), so the inherited legacy
22
- * `columns: 2` default no longer pins tablet to 2.
23
18
  * @returns The resolved number of columns for the active breakpoint.
24
19
  */
25
20
  export const useProductGridColumns = () => {
@@ -30,20 +25,24 @@ export const useProductGridColumns = () => {
30
25
  } = useContext(WidgetContext);
31
26
  const isInsideWidget = Boolean(widgetCode);
32
27
  const areAppSettingsHydrated = useSelector(getAreAppSettingsHydrated);
33
- // The built-in default { xs: 2, md: 4 } until hydrated, the source value after.
28
+ // The built-in default { small: 2, large: 4 } until hydrated, the source value after.
34
29
  const appSettingsColumns = useSelector(getProductGridColumns);
35
30
  const {
36
31
  columns: legacyColumns
37
32
  } = useWidgetSettings(WIDGET_ID) || {};
38
- const breakpoints = useMemo(() => {
33
+ const sizes = useMemo(() => {
39
34
  // Only special case: pre-hydration, outside a widget, honor the legacy
40
35
  // scalar flat across every breakpoint.
41
36
  if (!areAppSettingsHydrated && !isInsideWidget && typeof legacyColumns === 'number') {
42
37
  return {
43
- xs: legacyColumns
38
+ small: legacyColumns
44
39
  };
45
40
  }
46
41
  return appSettingsColumns;
47
42
  }, [areAppSettingsHydrated, isInsideWidget, legacyColumns, appSettingsColumns]);
43
+ const breakpoints = useMemo(() => Object.entries(sizes).reduce((acc, [size, value]) => {
44
+ acc[SCREEN_SIZE_BREAKPOINTS[size]] = value;
45
+ return acc;
46
+ }, {}), [sizes]);
48
47
  return useResponsiveValue(breakpoints);
49
48
  };
@@ -0,0 +1,42 @@
1
+ import type { ImageProps } from '@shopgate/pwa-common/components/Image';
2
+ import type { ImageResolution, ProductImageContext } from '@shopgate/engage/settings/types/appSettings';
3
+ /**
4
+ * Everything the underlying Image accepts is forwarded, minus the two this component owns:
5
+ * `backgroundColor`, derived from `noBackground`, and `placeholder`.
6
+ */
7
+ export interface ProductImageProps extends Omit<ImageProps, 'backgroundColor' | 'placeholder'> {
8
+ /**
9
+ * The surface this image is rendered on. Determines the resolutions and aspect ratio, which are
10
+ * configured app wide. Ignored when an explicit `resolutions` prop is passed.
11
+ */
12
+ context?: ProductImageContext | null;
13
+ /**
14
+ * Drops the background behind the image.
15
+ */
16
+ noBackground?: boolean;
17
+ /**
18
+ * Image to show instead of the placeholder icon. The shop wide setting wins over this.
19
+ */
20
+ placeholderSrc?: string | null;
21
+ /**
22
+ * @deprecated Pass a `context` instead, so the image follows the configured aspect ratio.
23
+ */
24
+ ratio?: number[] | null;
25
+ /**
26
+ * @deprecated Pass a `context` instead, so the image follows the configured aspect ratio.
27
+ * Still honored, and still takes precedence over `context`.
28
+ */
29
+ resolutions?: ImageResolution[];
30
+ }
31
+ /**
32
+ * The product image component.
33
+ *
34
+ * Behaves like the core Image component, with a product placeholder for a missing or failed image.
35
+ * @param props The component props.
36
+ * @returns The rendered component.
37
+ */
38
+ declare const ProductImage: (props: ProductImageProps) => import("react").JSX.Element;
39
+ export { ProductImage as UnwrappedProductImage };
40
+ declare const _default: import("react").MemoExoticComponent<(props: ProductImageProps) => import("react").JSX.Element>;
41
+ export default _default;
42
+ //# sourceMappingURL=ProductImage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProductImage.d.ts","sourceRoot":"","sources":["../../../../product/components/ProductImage/ProductImage.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uCAAuC,CAAC;AAMxE,OAAO,KAAK,EACV,eAAe,EACf,mBAAmB,EACpB,MAAM,6CAA6C,CAAC;AA0BrD;;;GAGG;AACH,MAAM,WAAW,iBAAkB,SAAQ,IAAI,CAAC,UAAU,EAAE,iBAAiB,GAAG,aAAa,CAAC;IAC5F;;;OAGG;IACH,OAAO,CAAC,EAAE,mBAAmB,GAAG,IAAI,CAAC;IACrC;;OAEG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IACxB;;;OAGG;IACH,WAAW,CAAC,EAAE,eAAe,EAAE,CAAC;CACjC;AAmCD;;;;;;GAMG;AACH,QAAA,MAAM,YAAY,GAAI,OAAO,iBAAiB,gCA0E7C,CAAC;AAEF,OAAO,EAAE,YAAY,IAAI,qBAAqB,EAAE,CAAC;oEAOX,iBAAiB;AAOvD,wBAA2C"}