@riosst100/pwa-marketplace 2.1.3 → 2.1.5

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 (57) hide show
  1. package/package.json +1 -1
  2. package/src/componentOverrideMapping.js +6 -0
  3. package/src/components/FavoriteSeller/AddToListButton/addToListButton.js +54 -54
  4. package/src/components/FavoriteSeller/AddToListButton/addToListButton.module.css +17 -17
  5. package/src/components/FavoriteSeller/AddToListButton/index.js +1 -1
  6. package/src/components/FavoriteSeller/AddToListButton/useCommonToasts.js +33 -33
  7. package/src/components/FilterTop/CustomFilters/customFilters.js +130 -132
  8. package/src/components/FilterTop/filterTop.js +1 -8
  9. package/src/components/LinkToOtherStores/index.js +61 -0
  10. package/src/components/NonSportCardsSets/nonSportCardsSets.js +0 -2
  11. package/src/components/RFQ/index.js +6 -3
  12. package/src/components/SellerDetail/sellerDetail.js +18 -1
  13. package/src/components/SellerInformation/sellerInformation.js +5 -3
  14. package/src/components/SellerSocialMedia/index.js +96 -0
  15. package/src/components/SetsData/index.js +2 -0
  16. package/src/components/SetsData/setsData.js +349 -0
  17. package/src/components/SetsData/setsData.module.css +76 -0
  18. package/src/components/SetsData/setsData.shimmer.js +50 -0
  19. package/src/components/ShopBy/shopBy.js +3 -2
  20. package/src/overwrites/pagebuilder/lib/ContentTypes/Products/products.js +13 -0
  21. package/src/overwrites/peregrine/lib/talons/MagentoRoute/magentoRoute.gql.js +27 -0
  22. package/src/overwrites/peregrine/lib/talons/MagentoRoute/useMagentoRoute.js +193 -0
  23. package/src/overwrites/peregrine/lib/talons/ProductFullDetail/useProductFullDetail.js +40 -9
  24. package/src/overwrites/peregrine/lib/talons/ProductImageCarousel/useProductImageCarousel.js +77 -0
  25. package/src/overwrites/peregrine/lib/talons/ProductOptions/useOption.js +59 -0
  26. package/src/overwrites/peregrine/lib/talons/ProductOptions/useTile.js +47 -0
  27. package/src/overwrites/peregrine/lib/talons/RootComponents/Category/categoryFragments.gql.js +13 -0
  28. package/src/overwrites/peregrine/lib/talons/RootComponents/Product/productDetailFragment.gql.js +23 -0
  29. package/src/overwrites/venia-ui/lib/RootComponents/Category/category.js +11 -3
  30. package/src/overwrites/venia-ui/lib/RootComponents/Category/categoryContent.js +0 -6
  31. package/src/overwrites/venia-ui/lib/components/AccountInformationPage/accountInformationPage.js +0 -1
  32. package/src/overwrites/venia-ui/lib/components/AccountInformationPage/editForm.js +0 -1
  33. package/src/overwrites/venia-ui/lib/components/Breadcrumbs/breadcrumbs.js +0 -3
  34. package/src/overwrites/venia-ui/lib/components/CheckoutPage/OrderSummary/orderSummary.js +0 -1
  35. package/src/overwrites/venia-ui/lib/components/Gallery/item.js +17 -3
  36. package/src/overwrites/venia-ui/lib/components/Price/price.js +113 -0
  37. package/src/overwrites/venia-ui/lib/components/ProductFullDetail/components/auctionDetail.js +1 -1
  38. package/src/overwrites/venia-ui/lib/components/ProductFullDetail/productFullDetail.js +77 -104
  39. package/src/overwrites/venia-ui/lib/components/ProductImageCarousel/carousel.js +3 -1
  40. package/src/overwrites/venia-ui/lib/components/ProductOptions/option.js +112 -0
  41. package/src/overwrites/venia-ui/lib/components/ProductOptions/option.module.css +30 -0
  42. package/src/overwrites/venia-ui/lib/components/ProductOptions/options.js +49 -0
  43. package/src/overwrites/venia-ui/lib/components/ProductOptions/tile.js +118 -0
  44. package/src/overwrites/venia-ui/lib/components/ProductOptions/tile.module.css +68 -0
  45. package/src/overwrites/venia-ui/lib/components/ProductOptions/tileList.js +78 -0
  46. package/src/overwrites/venia-ui/lib/components/ProductOptions/tileList.module.css +6 -0
  47. package/src/overwrites/venia-ui/lib/components/ProductOptions/tileList.shimmer.js +32 -0
  48. package/src/talons/CustomFilters/useCustomFilters.js +0 -2
  49. package/src/talons/FavoriteSeller/AddToListButton/addToListButton.gql.js +30 -30
  50. package/src/talons/FavoriteSeller/AddToListButton/useAddToFavoriteListButton.js +0 -1
  51. package/src/talons/LegoSets/useLegoSets.js +0 -5
  52. package/src/talons/Seller/useSeller.js +1 -1
  53. package/src/talons/SetsData/setsData.gql.js +70 -0
  54. package/src/talons/SetsData/useSetsData.js +225 -0
  55. package/src/talons/ShopBy/shopBy.gql.js +12 -6
  56. package/src/talons/ShopBy/useShopBy.js +13 -4
  57. package/src/talons/TrainsSets/useTrainsSets.js +0 -3
@@ -81,7 +81,8 @@ const GalleryItem = props => {
81
81
 
82
82
  // fallback to regular price when final price is unavailable
83
83
  const priceSource =
84
- price_range.maximum_price.final_price ||
84
+ price_range.minimum_price?.final_price ||
85
+ price_range.minimum_price?.regular_price || price_range.maximum_price.final_price ||
85
86
  price_range.maximum_price.regular_price;
86
87
 
87
88
  // Hide the Rating component until it is updated with the new look and feel (PWA-2512).
@@ -144,12 +145,12 @@ const GalleryItem = props => {
144
145
  currencyCode={priceSource.currency}
145
146
  />
146
147
  </p>
147
- <p className='old-price text-gray-400 text-xs line-through font-normal !mb-0'>
148
+ {/* <p className='old-price text-gray-400 text-xs line-through font-normal !mb-0'>
148
149
  <Price
149
150
  value={priceSource.value}
150
151
  currencyCode={priceSource.currency}
151
152
  />
152
- </p>
153
+ </p> */}
153
154
  </div>
154
155
 
155
156
  <div data-cy="GalleryItem-Rating" className={cn('flex gap-[5px] items-center mb-2')}>
@@ -212,6 +213,19 @@ GalleryItem.propTypes = {
212
213
  discount: shape({
213
214
  amount_off: number.isRequired
214
215
  }).isRequired
216
+ }).isRequired,
217
+ minimum_price: shape({
218
+ final_price: shape({
219
+ value: number.isRequired,
220
+ currency: string.isRequired
221
+ }),
222
+ regular_price: shape({
223
+ value: number.isRequired,
224
+ currency: string.isRequired
225
+ }).isRequired,
226
+ discount: shape({
227
+ amount_off: number.isRequired
228
+ }).isRequired
215
229
  }).isRequired
216
230
  }).isRequired
217
231
  }),
@@ -0,0 +1,113 @@
1
+ import React, { Fragment } from 'react';
2
+ import { number, string, shape } from 'prop-types';
3
+ import { useIntl } from 'react-intl';
4
+ import patches from '@magento/peregrine/lib/util/intlPatches';
5
+
6
+ /**
7
+ * The **Price** component is used anywhere a price needs to be displayed.
8
+ *
9
+ * Formatting of prices and currency symbol selection is handled entirely by the ECMAScript Internationalization API available in modern browsers.
10
+ *
11
+ * A [polyfill][] is required for any JavaScript runtime that does not have [Intl.NumberFormat.prototype.formatToParts][].
12
+ *
13
+ * [polyfill]: https://www.npmjs.com/package/intl
14
+ * [Intl.NumberFormat.prototype.formatToParts]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat/formatToParts
15
+ */
16
+
17
+ const Price = props => {
18
+ const { locale } = useIntl();
19
+ const { value, currencyCode, classes } = props;
20
+
21
+ // Check if the value contains " - "
22
+ if (typeof value === 'string' && value.includes(" - ")) {
23
+ const explodedValues = value.split(" - ");
24
+ const parts = patches.toParts.call(
25
+ new Intl.NumberFormat(locale, {
26
+ style: 'currency',
27
+ currency: currencyCode
28
+ }),
29
+ explodedValues[0]
30
+ );
31
+
32
+ const children = parts.map((part, i) => {
33
+ const partClass = classes[part.type];
34
+ const key = `${i}-${part.value}`;
35
+
36
+ return (
37
+ <span key={key} className={partClass}>
38
+ {part.value}
39
+ </span>
40
+ );
41
+ });
42
+
43
+ const parts2 = patches.toParts.call(
44
+ new Intl.NumberFormat(locale, {
45
+ style: 'currency',
46
+ currency: currencyCode
47
+ }),
48
+ explodedValues[1]
49
+ );
50
+
51
+ const children2 = parts2.map((part, i) => {
52
+ const partClass = classes[part.type];
53
+ const key = `${i}-${part.value}`;
54
+
55
+ return (
56
+ <span key={key} className={partClass}>
57
+ {part.value}
58
+ </span>
59
+ );
60
+ });
61
+
62
+ return <Fragment>{children} - {children2}</Fragment>;
63
+ } else {
64
+ const parts = patches.toParts.call(
65
+ new Intl.NumberFormat(locale, {
66
+ style: 'currency',
67
+ currency: currencyCode
68
+ }),
69
+ value
70
+ );
71
+
72
+ const children = parts.map((part, i) => {
73
+ const partClass = classes[part.type];
74
+ const key = `${i}-${part.value}`;
75
+
76
+ return (
77
+ <span key={key} className={partClass}>
78
+ {part.value}
79
+ </span>
80
+ );
81
+ });
82
+
83
+ return <Fragment>{children}</Fragment>;
84
+ }
85
+
86
+ return null;
87
+ };
88
+
89
+ Price.propTypes = {
90
+ /**
91
+ * Class names to use when styling this component
92
+ */
93
+ classes: shape({
94
+ currency: string,
95
+ integer: string,
96
+ decimal: string,
97
+ fraction: string
98
+ }),
99
+ /**
100
+ * The numeric price
101
+ */
102
+ value: number.isRequired,
103
+ /**
104
+ * A string with any of the currency code supported by Intl.NumberFormat
105
+ */
106
+ currencyCode: string.isRequired
107
+ };
108
+
109
+ Price.defaultProps = {
110
+ classes: {}
111
+ };
112
+
113
+ export default Price;
@@ -71,7 +71,7 @@ const AuctionDetail = (props) => {
71
71
  }
72
72
  };
73
73
 
74
- console.log(auctionData)
74
+ // console.log(auctionData)
75
75
 
76
76
  const endDate = auctionData && auctionData.stop_auction_time ? new Date(auctionData.stop_auction_time) : null;
77
77
 
@@ -1,4 +1,4 @@
1
- import React, { useMemo, Fragment, Suspense } from 'react';
1
+ import React, { useState, useMemo, Fragment, Suspense } from 'react';
2
2
  import { FormattedMessage, useIntl } from 'react-intl';
3
3
  import { arrayOf, bool, number, shape, string } from 'prop-types';
4
4
  import { Form } from 'informed';
@@ -36,6 +36,7 @@ import CrossSeller from '@riosst100/pwa-marketplace/src/components/CrossSeller';
36
36
  import RelatedProduct from '@riosst100/pwa-marketplace/src/components/RelatedProduct';
37
37
  import ProductLabel from '@riosst100/pwa-marketplace/src/components/ProductLabel';
38
38
  import RFQ from '@riosst100/pwa-marketplace/src/components/RFQ';
39
+ import LinkToOtherStores from '@riosst100/pwa-marketplace/src/components/LinkToOtherStores';
39
40
  import Collapsible from '@riosst100/pwa-marketplace/src/components/commons/Collapsible';
40
41
 
41
42
  // Correlate a GQL error message to a field. GQL could return a longer error
@@ -87,22 +88,26 @@ const ProductFullDetail = props => {
87
88
  isAddToCartDisabled,
88
89
  isSupportedProductType,
89
90
  mediaGalleryEntries,
91
+ selectedMedia,
90
92
  productDetails,
91
93
  customAttributes,
92
94
  wishlistButtonProps,
93
95
  sellerDetails
94
96
  } = talonProps;
95
- console.log("🚀 ~ ProductFullDetail ~ talonProps:", talonProps)
96
97
 
97
98
  const { formatMessage } = useIntl();
98
99
 
99
100
  const classes = useStyle(defaultClasses, props.classes);
100
101
 
102
+ const [hoveredMedia, setHoveredMedia] = useState(null);
103
+
101
104
  const options = isProductConfigurable(product) ? (
102
105
  <Suspense fallback={<ProductOptionsShimmer />}>
103
106
  <Options
104
107
  onSelectionChange={handleSelectionChange}
105
108
  options={product.configurable_options}
109
+ setHoveredMedia={setHoveredMedia}
110
+ variants={product.variants}
106
111
  isEverythingOutOfStock={isEverythingOutOfStock}
107
112
  outOfStockVariants={outOfStockVariants}
108
113
  />
@@ -223,28 +228,7 @@ const ProductFullDetail = props => {
223
228
  // Error message for screen reader
224
229
  const cartActionContent = isSupportedProductType ? (
225
230
  <section className={cn(classes.actButton, 'flex gap-x-[30px]')}>
226
- <Button
227
- data-cy="ProductFullDetail-addToCartButton"
228
- disabled={isAddToCartDisabled}
229
- aria-disabled={isAddToCartDisabled}
230
- aria-label={
231
- isEverythingOutOfStock
232
- ? formatMessage({
233
- id: 'productFullDetail.outOfStockProduct',
234
- defaultMessage:
235
- 'This item is currently out of stock'
236
- })
237
- : ''
238
- }
239
- classes={{
240
- content: 'normal-case font-medium text-[16px]'
241
- }}
242
- priority="low"
243
- type="submit"
244
- >
245
- Make An Offer
246
- </Button>
247
-
231
+ <RFQ disabled={isAddToCartDisabled} />
248
232
  <Button
249
233
  data-cy="ProductFullDetail-addToCartButton"
250
234
  disabled={isAddToCartDisabled}
@@ -399,43 +383,60 @@ const ProductFullDetail = props => {
399
383
 
400
384
 
401
385
  const dataTabs =
402
- [
403
- {
404
- id: 'product-detail',
405
- title: 'Description',
406
- content: <ProductDescription />
407
- },
408
- {
409
- id: 'product-more-info',
410
- title: 'Details',
411
- content: <ProductMoreInfo />
412
- },
413
- {
414
- id: 'product-tnc',
415
- title: 'Term & Conditions',
416
- content: <ProductTNC />
417
- },
418
- {
419
- id: 'product-shipping-policy',
420
- title: 'Shipping Policy',
421
- content: <ShippingPolicy />
422
- },
423
- {
424
- id: 'product-return-policy',
425
- title: 'Return Policy',
426
- content: <ReturnPolicy />
427
- },
428
- {
429
- id: 'product-faq',
430
- title: 'FAQ',
431
- content: <ProductFAQ />
432
- },
433
- {
434
- id: 'product-reviews',
435
- title: 'Reviews',
436
- content: <ProductReviews className={cn(contentContainerClass)} />
437
- }
438
- ];
386
+ [
387
+ {
388
+ id: 'product-detail',
389
+ title: 'Description',
390
+ content: <ProductDescription />
391
+ },
392
+ {
393
+ id: 'product-more-info',
394
+ title: 'Details',
395
+ content: <ProductMoreInfo />
396
+ },
397
+ {
398
+ id: 'product-tnc',
399
+ title: 'Term & Conditions',
400
+ content: <ProductTNC />
401
+ },
402
+ {
403
+ id: 'product-shipping-policy',
404
+ title: 'Shipping Policy',
405
+ content: <ShippingPolicy />
406
+ },
407
+ {
408
+ id: 'product-return-policy',
409
+ title: 'Return Policy',
410
+ content: <ReturnPolicy />
411
+ },
412
+ {
413
+ id: 'product-faq',
414
+ title: 'FAQ',
415
+ content: <ProductFAQ />
416
+ },
417
+ {
418
+ id: 'product-reviews',
419
+ title: 'Reviews',
420
+ content: <ProductReviews className={cn(contentContainerClass)} />
421
+ }
422
+ ];
423
+
424
+ const getSellerAddressDisplay = (seller) => {
425
+ let city = seller?.city;
426
+ let country = seller?.country;
427
+
428
+ let result = '';
429
+ if (city && country) {
430
+ result = `${city}, ${country}`;
431
+ }
432
+ if (city && !country) {
433
+ result = `${city}`;
434
+ }
435
+ if (!city && country) {
436
+ result = `${country}`;
437
+ }
438
+ return result;
439
+ }
439
440
 
440
441
  return (
441
442
  <Fragment>
@@ -447,7 +448,7 @@ const ProductFullDetail = props => {
447
448
  >
448
449
  <section className={cn(classes.leftContainer, 'relative')}>
449
450
  <ProductLabel item={productDetails} />
450
- <Carousel images={mediaGalleryEntries} />
451
+ <Carousel images={mediaGalleryEntries} hoveredMedia={hoveredMedia} selectedMedia={selectedMedia}/>
451
452
  <div className='product_group-actions flex gap-x-[18px] gap-y-4 justify-center items-center mt-2 lg_mt-5 mb-6 lg_mb-0'>
452
453
  <Suspense fallback={null}>
453
454
  <WishlistButton
@@ -469,7 +470,7 @@ const ProductFullDetail = props => {
469
470
  <div
470
471
  className={cn(
471
472
  classes.title,
472
- 'mb-[30px]',
473
+ 'mb-xs',
473
474
  )}
474
475
  >
475
476
  <h1
@@ -500,7 +501,7 @@ const ProductFullDetail = props => {
500
501
  <div
501
502
  className={cn(
502
503
  'product_short_description',
503
- 'mb-[30px]',
504
+ 'mb-xs',
504
505
  )}
505
506
  >
506
507
  {shortDescription}
@@ -514,7 +515,7 @@ const ProductFullDetail = props => {
514
515
  <div
515
516
  className={cn(
516
517
  'product_price_container',
517
- 'py-[30px] flex justify-between flex-wrap gap-y-5'
518
+ 'py-xs flex justify-between flex-wrap gap-y-5'
518
519
  )}
519
520
  >
520
521
  <div className='flex flex-col gap-y-5 max-w-[448px]'>
@@ -528,10 +529,11 @@ const ProductFullDetail = props => {
528
529
  >
529
530
  <Price
530
531
  currencyCode={productDetails.price.currency}
532
+ priceRange={productDetails.price_range}
531
533
  value={productDetails.price.value}
532
534
  />
533
535
  </p>
534
- <p
536
+ {/* <p
535
537
  data-cy="ProductFullDetail-productOldPrice"
536
538
  className={cn(
537
539
  classes.productPrice,
@@ -542,7 +544,7 @@ const ProductFullDetail = props => {
542
544
  currencyCode={productDetails.price.currency}
543
545
  value={productDetails.price.value}
544
546
  />
545
- </p>
547
+ </p> */}
546
548
  </div>
547
549
  <AuctionDetail className="auction_detail-container" />
548
550
  <PreorderDetail className={'preorder_detail-container'} />
@@ -564,7 +566,7 @@ const ProductFullDetail = props => {
564
566
  errors={errors.get('form') || []}
565
567
  />
566
568
  <section className={classes.options}>{options}</section>
567
- <section className={cn(classes.quantity, 'py-[30px] !border-none')}>
569
+ <section className={cn(classes.quantity, 'py-xs !border-none')}>
568
570
  {/* <span
569
571
  data-cy="ProductFullDetail-quantityTitle"
570
572
  className={classes.quantityTitle}
@@ -579,7 +581,7 @@ const ProductFullDetail = props => {
579
581
  message={errors.get('quantity')}
580
582
  />
581
583
 
582
- <div className='product_shipping-information mb-[30px] leading-[18px] mt-[25px]'>
584
+ {/* <div className='product_shipping-information mb-[30px] leading-[18px] mt-[25px]'>
583
585
  {sellerDetails &&
584
586
  <div className='text-xs'>
585
587
  Ship From <span className='font-medium'>{sellerDetails.country}</span>
@@ -590,48 +592,19 @@ const ProductFullDetail = props => {
590
592
  <div className='text-xs'>
591
593
  Shiping Method <span className='font-medium'>Store Pick Up | Meet Up</span>
592
594
  </div>
593
- </div>
595
+ </div> */}
594
596
 
595
597
  <div className='product_actions-wrapper'>
596
598
  {cartActionContent}
597
599
  </div>
598
600
  </section>
599
- <Divider />
601
+ {/* <Divider />
600
602
  <section className={cn(classes.quantity, 'py-[30px] !border-none')}>
601
603
  <RFQ />
602
- </section>
603
- <Divider />
604
- <section className='product_from-other-platform py-[30px]'>
605
- <div className='text-sm flex mb-5'>
606
- Also available in
607
- </div>
608
- <div className='platform-container flex gap-x-2.5'>
609
- <Link
610
- className='platform_logo-wrapper rounded-md px-5 py-2.5 border border-gray-100 flex items-center'
611
- to=""
612
- >
613
- <img
614
- alt="tokopedia"
615
- width={60}
616
- height={30}
617
- src={'https://upload.wikimedia.org/wikipedia/commons/a/a7/Tokopedia.svg'}
618
- />
619
- </Link>
620
- <Link
621
- className='platform_logo-wrapper rounded-md px-5 py-2.5 border border-gray-100 flex items-center'
622
- to=""
623
- >
624
- <img
625
- alt="shopee"
626
- width={60}
627
- height={30}
628
- src={'https://upload.wikimedia.org/wikipedia/commons/f/fe/Shopee.svg'}
629
- />
630
- </Link>
631
- </div>
632
- </section>
604
+ </section> */}
633
605
  <Divider />
634
- <section className='seller-information py-[30px]'>
606
+ <LinkToOtherStores productDetails={productDetails} />
607
+ <section className='seller-information py-xs'>
635
608
  <div className='flex xs_flex-col md_flex-row xs_items-center md_items-start items-start gap-[15px] relative'>
636
609
  <div className='flex flex-row justify-between relative w-full'>
637
610
  <div className='flex flex-col xs_items-center md_items-start gap-[6px] relative'>
@@ -644,7 +617,7 @@ const ProductFullDetail = props => {
644
617
  </div>
645
618
  </div>
646
619
  <div class="relative w-fit font-normal text-[#999999] text-[12px] tracking-[0] leading-[14px] whitespace-nowrap">
647
- {sellerDetails ? sellerDetails.city + ', ' + sellerDetails.country : ''}
620
+ {sellerDetails ? getSellerAddressDisplay(sellerDetails) : ''}
648
621
  </div>
649
622
  </div>
650
623
  <div className='flex flex-wrap items-start gap-4 relative'>
@@ -32,10 +32,12 @@ const IMAGE_WIDTH = 495;
32
32
  * @returns {React.Element} React carousel component that displays a product image
33
33
  */
34
34
  const ProductImageCarousel = props => {
35
- const { images } = props;
35
+ const { images, selectedMedia, hoveredMedia } = props;
36
36
  const { formatMessage } = useIntl();
37
37
  const talonProps = useProductImageCarousel({
38
38
  images,
39
+ selectedMedia,
40
+ hoveredMedia,
39
41
  imageWidth: IMAGE_WIDTH
40
42
  });
41
43
 
@@ -0,0 +1,112 @@
1
+ import React, { useMemo } from 'react';
2
+ import { FormattedMessage } from 'react-intl';
3
+ import {
4
+ arrayOf,
5
+ func,
6
+ number,
7
+ object,
8
+ oneOfType,
9
+ shape,
10
+ string
11
+ } from 'prop-types';
12
+
13
+ import { useStyle } from '@magento/venia-ui/lib/classify';
14
+ import getOptionType from '@magento/venia-ui/lib/components/ProductOptions/getOptionType';
15
+ import SwatchList from '@magento/venia-ui/lib/components/ProductOptions/swatchList';
16
+ import TileList from './tileList';
17
+ import defaultClasses from './option.module.css';
18
+ import { useOption } from '@magento/peregrine/lib/talons/ProductOptions/useOption';
19
+
20
+ const getItemKey = ({ value_index }) => value_index;
21
+
22
+ // TODO: get an explicit field from the API
23
+ // that identifies an attribute as a swatch
24
+ const getListComponent = (attribute_code, values) => {
25
+ const optionType = getOptionType({ attribute_code, values });
26
+
27
+ return optionType === 'swatch' ? SwatchList : TileList;
28
+ };
29
+
30
+ const Option = props => {
31
+ const {
32
+ attribute_code,
33
+ attribute_id,
34
+ label,
35
+ onSelectionChange,
36
+ selectedValue,
37
+ values,
38
+ variants,
39
+ setHoveredMedia,
40
+ isEverythingOutOfStock,
41
+ outOfStockVariants
42
+ } = props;
43
+
44
+ const talonProps = useOption({
45
+ attribute_id,
46
+ attribute_code,
47
+ label,
48
+ variants,
49
+ onSelectionChange,
50
+ selectedValue,
51
+ values
52
+ });
53
+
54
+ const {
55
+ handleSelectionChange,
56
+ initialSelection,
57
+ selectedValueDescription,
58
+ filteredVariants
59
+ } = talonProps;
60
+
61
+ const ValueList = useMemo(() => getListComponent(attribute_code, values), [
62
+ attribute_code,
63
+ values
64
+ ]);
65
+
66
+ const classes = useStyle(defaultClasses, props.classes);
67
+
68
+ return (
69
+ <div className={classes.root} data-cy="ProductOptions-Option-root">
70
+ <span className={classes.title}>{label}</span>
71
+ <ValueList
72
+ getItemKey={getItemKey}
73
+ selectedValue={initialSelection}
74
+ items={values}
75
+ setHoveredMedia={setHoveredMedia}
76
+ onSelectionChange={handleSelectionChange}
77
+ isEverythingOutOfStock={isEverythingOutOfStock}
78
+ outOfStockVariants={outOfStockVariants}
79
+ filteredVariants={filteredVariants}
80
+ attributeLabel={label}
81
+ />
82
+ <dl className={classes.selection} style={{ display: 'none' }}>
83
+ <dt
84
+ data-cy="ProductOptions-Option-selectedLabel"
85
+ className={classes.selectionLabel}
86
+ >
87
+ <FormattedMessage
88
+ id="productOptions.selectedLabel"
89
+ defaultMessage="Selected {label}:"
90
+ values={{ label }}
91
+ />
92
+ </dt>
93
+ <dd>{selectedValueDescription}</dd>
94
+ </dl>
95
+ </div>
96
+ );
97
+ };
98
+
99
+ Option.propTypes = {
100
+ attribute_code: string.isRequired,
101
+ attribute_id: string,
102
+ classes: shape({
103
+ root: string,
104
+ title: string
105
+ }),
106
+ label: string.isRequired,
107
+ onSelectionChange: func,
108
+ selectedValue: oneOfType([number, string]),
109
+ values: arrayOf(object).isRequired
110
+ };
111
+
112
+ export default Option;
@@ -0,0 +1,30 @@
1
+ .root {
2
+ composes: border-b from global;
3
+ composes: border-solid from global;
4
+ /* composes: border-subtle from global; */
5
+ /* composes: mx-sm from global; */
6
+ composes: my-0 from global;
7
+ composes: px-0 from global;
8
+ composes: py-xs from global;
9
+
10
+ border-color: rgb(230 233 234);
11
+ }
12
+
13
+ .title {
14
+ composes: block from global;
15
+ composes: font-semibold from global;
16
+ composes: leading-normal from global;
17
+ composes: mb-xs from global;
18
+ composes: text-colorDefault from global;
19
+ }
20
+
21
+ .selection {
22
+ composes: flex from global;
23
+ composes: leading-normal from global;
24
+ composes: mt-xs from global;
25
+ composes: text-colorDefault from global;
26
+ }
27
+
28
+ .selectionLabel {
29
+ composes: mr-xs from global;
30
+ }
@@ -0,0 +1,49 @@
1
+ import React from 'react';
2
+ import { array, func } from 'prop-types';
3
+
4
+ import Option from './option';
5
+ import { useOptions } from '@magento/peregrine/lib/talons/ProductOptions/useOptions';
6
+
7
+ const Options = props => {
8
+ const {
9
+ classes,
10
+ onSelectionChange,
11
+ options,
12
+ variants,
13
+ setHoveredMedia,
14
+ selectedValues = [],
15
+ isEverythingOutOfStock,
16
+ outOfStockVariants
17
+ } = props;
18
+
19
+ const talonProps = useOptions({
20
+ onSelectionChange,
21
+ selectedValues,
22
+ options
23
+ });
24
+
25
+ const { handleSelectionChange, selectedValueMap } = talonProps;
26
+
27
+ // Render a list of options passing in any pre-selected values.
28
+ return options.map(option => (
29
+ <Option
30
+ {...option}
31
+ classes={classes}
32
+ key={option.attribute_id}
33
+ variants={variants}
34
+ setHoveredMedia={setHoveredMedia}
35
+ onSelectionChange={handleSelectionChange}
36
+ selectedValue={selectedValueMap.get(option.label)}
37
+ isEverythingOutOfStock={isEverythingOutOfStock}
38
+ outOfStockVariants={outOfStockVariants}
39
+ />
40
+ ));
41
+ };
42
+
43
+ Options.propTypes = {
44
+ onSelectionChange: func,
45
+ options: array.isRequired,
46
+ selectedValues: array
47
+ };
48
+
49
+ export default Options;