@riosst100/pwa-marketplace 2.1.4 → 2.1.6

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 (48) hide show
  1. package/package.json +1 -1
  2. package/src/componentOverrideMapping.js +7 -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/overwrites/pagebuilder/lib/ContentTypes/Products/products.js +13 -0
  16. package/src/overwrites/peregrine/lib/talons/MagentoRoute/magentoRoute.gql.js +27 -0
  17. package/src/overwrites/peregrine/lib/talons/MagentoRoute/useMagentoRoute.js +193 -0
  18. package/src/overwrites/peregrine/lib/talons/ProductFullDetail/useProductFullDetail.js +48 -11
  19. package/src/overwrites/peregrine/lib/talons/ProductImageCarousel/useProductImageCarousel.js +77 -0
  20. package/src/overwrites/peregrine/lib/talons/ProductOptions/useOption.js +59 -0
  21. package/src/overwrites/peregrine/lib/talons/ProductOptions/useTile.js +47 -0
  22. package/src/overwrites/peregrine/lib/talons/RootComponents/Category/categoryFragments.gql.js +13 -0
  23. package/src/overwrites/peregrine/lib/talons/RootComponents/Product/product.gql.js +5 -2
  24. package/src/overwrites/peregrine/lib/talons/RootComponents/Product/productDetailFragment.gql.js +23 -0
  25. package/src/overwrites/peregrine/lib/talons/RootComponents/Product/useProduct.js +121 -0
  26. package/src/overwrites/venia-ui/lib/RootComponents/Category/categoryContent.js +0 -6
  27. package/src/overwrites/venia-ui/lib/components/AccountInformationPage/accountInformationPage.js +0 -1
  28. package/src/overwrites/venia-ui/lib/components/AccountInformationPage/editForm.js +0 -1
  29. package/src/overwrites/venia-ui/lib/components/Breadcrumbs/breadcrumbs.js +0 -3
  30. package/src/overwrites/venia-ui/lib/components/CheckoutPage/OrderSummary/orderSummary.js +0 -1
  31. package/src/overwrites/venia-ui/lib/components/Gallery/item.js +17 -3
  32. package/src/overwrites/venia-ui/lib/components/Price/price.js +113 -0
  33. package/src/overwrites/venia-ui/lib/components/ProductFullDetail/components/auctionDetail.js +1 -1
  34. package/src/overwrites/venia-ui/lib/components/ProductFullDetail/productFullDetail.js +107 -105
  35. package/src/overwrites/venia-ui/lib/components/ProductImageCarousel/carousel.js +3 -1
  36. package/src/overwrites/venia-ui/lib/components/ProductOptions/option.js +112 -0
  37. package/src/overwrites/venia-ui/lib/components/ProductOptions/option.module.css +30 -0
  38. package/src/overwrites/venia-ui/lib/components/ProductOptions/options.js +49 -0
  39. package/src/overwrites/venia-ui/lib/components/ProductOptions/tile.js +118 -0
  40. package/src/overwrites/venia-ui/lib/components/ProductOptions/tile.module.css +68 -0
  41. package/src/overwrites/venia-ui/lib/components/ProductOptions/tileList.js +78 -0
  42. package/src/overwrites/venia-ui/lib/components/ProductOptions/tileList.module.css +6 -0
  43. package/src/overwrites/venia-ui/lib/components/ProductOptions/tileList.shimmer.js +32 -0
  44. package/src/talons/CustomFilters/useCustomFilters.js +0 -2
  45. package/src/talons/FavoriteSeller/AddToListButton/addToListButton.gql.js +30 -30
  46. package/src/talons/FavoriteSeller/AddToListButton/useAddToFavoriteListButton.js +0 -1
  47. package/src/talons/LegoSets/useLegoSets.js +0 -5
  48. package/src/talons/TrainsSets/useTrainsSets.js +0 -3
@@ -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,7 +36,9 @@ 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';
41
+ import { useLocation } from 'react-router-dom';
40
42
 
41
43
  // Correlate a GQL error message to a field. GQL could return a longer error
42
44
  // string but it may contain contextual info such as product id. We can use
@@ -74,7 +76,12 @@ const ProductDetailsCollapsible = (props) => {
74
76
  const ProductFullDetail = props => {
75
77
  const { product } = props;
76
78
 
77
- const talonProps = useProductFullDetail({ product });
79
+ const { search } = useLocation();
80
+
81
+ const params = new URLSearchParams(search);
82
+ const isPreview = params.get('preview');
83
+
84
+ const talonProps = useProductFullDetail({ product, isPreview });
78
85
 
79
86
  const {
80
87
  breadcrumbCategoryId,
@@ -87,22 +94,26 @@ const ProductFullDetail = props => {
87
94
  isAddToCartDisabled,
88
95
  isSupportedProductType,
89
96
  mediaGalleryEntries,
97
+ selectedMedia,
90
98
  productDetails,
91
99
  customAttributes,
92
100
  wishlistButtonProps,
93
101
  sellerDetails
94
102
  } = talonProps;
95
- console.log("🚀 ~ ProductFullDetail ~ talonProps:", talonProps)
96
103
 
97
104
  const { formatMessage } = useIntl();
98
105
 
99
106
  const classes = useStyle(defaultClasses, props.classes);
100
107
 
108
+ const [hoveredMedia, setHoveredMedia] = useState(null);
109
+
101
110
  const options = isProductConfigurable(product) ? (
102
111
  <Suspense fallback={<ProductOptionsShimmer />}>
103
112
  <Options
104
113
  onSelectionChange={handleSelectionChange}
105
114
  options={product.configurable_options}
115
+ setHoveredMedia={setHoveredMedia}
116
+ variants={product.variants}
106
117
  isEverythingOutOfStock={isEverythingOutOfStock}
107
118
  outOfStockVariants={outOfStockVariants}
108
119
  />
@@ -223,28 +234,7 @@ const ProductFullDetail = props => {
223
234
  // Error message for screen reader
224
235
  const cartActionContent = isSupportedProductType ? (
225
236
  <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
-
237
+ <RFQ disabled={isAddToCartDisabled} />
248
238
  <Button
249
239
  data-cy="ProductFullDetail-addToCartButton"
250
240
  disabled={isAddToCartDisabled}
@@ -399,47 +389,87 @@ const ProductFullDetail = props => {
399
389
 
400
390
 
401
391
  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
- ];
392
+ [
393
+ {
394
+ id: 'product-detail',
395
+ title: 'Description',
396
+ content: <ProductDescription />
397
+ },
398
+ {
399
+ id: 'product-more-info',
400
+ title: 'Details',
401
+ content: <ProductMoreInfo />
402
+ },
403
+ {
404
+ id: 'product-tnc',
405
+ title: 'Term & Conditions',
406
+ content: <ProductTNC />
407
+ },
408
+ {
409
+ id: 'product-shipping-policy',
410
+ title: 'Shipping Policy',
411
+ content: <ShippingPolicy />
412
+ },
413
+ {
414
+ id: 'product-return-policy',
415
+ title: 'Return Policy',
416
+ content: <ReturnPolicy />
417
+ },
418
+ {
419
+ id: 'product-faq',
420
+ title: 'FAQ',
421
+ content: <ProductFAQ />
422
+ },
423
+ {
424
+ id: 'product-reviews',
425
+ title: 'Reviews',
426
+ content: <ProductReviews className={cn(contentContainerClass)} />
427
+ }
428
+ ];
429
+
430
+ const getSellerAddressDisplay = (seller) => {
431
+ let city = seller?.city;
432
+ let country = seller?.country;
433
+
434
+ let result = '';
435
+ if (city && country) {
436
+ result = `${city}, ${country}`;
437
+ }
438
+ if (city && !country) {
439
+ result = `${city}`;
440
+ }
441
+ if (!city && country) {
442
+ result = `${country}`;
443
+ }
444
+ return result;
445
+ }
446
+
447
+ const productPreviewMessages = isPreview ? (
448
+ <div
449
+ style={{
450
+ backgroundColor: "white", // Light gray background
451
+ border: "1px solid rgb(242 101 102)", // Subtle border
452
+ borderRadius: "5px", // Rounded corners
453
+ padding: "10px 15px", // Space inside the box
454
+ margin: "10px 0px 20px 0", // Space outside the box
455
+ fontSize: "14px", // Readable text size
456
+ color: "rgb(242 101 102)", // Dark gray text
457
+ }}
458
+ >
459
+ <span
460
+ style={{
461
+ fontWeight: "500", // Slightly bold text
462
+ }}
463
+ >
464
+ This is a preview of your product. Only you can see this page.
465
+ </span>
466
+ </div>
467
+ ) : null;
439
468
 
440
469
  return (
441
470
  <Fragment>
442
471
  {breadcrumbs}
472
+ {productPreviewMessages}
443
473
  <Form
444
474
  className={classes.root}
445
475
  data-cy="ProductFullDetail-root"
@@ -447,7 +477,7 @@ const ProductFullDetail = props => {
447
477
  >
448
478
  <section className={cn(classes.leftContainer, 'relative')}>
449
479
  <ProductLabel item={productDetails} />
450
- <Carousel images={mediaGalleryEntries} />
480
+ <Carousel images={mediaGalleryEntries} hoveredMedia={hoveredMedia} selectedMedia={selectedMedia}/>
451
481
  <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
482
  <Suspense fallback={null}>
453
483
  <WishlistButton
@@ -469,7 +499,7 @@ const ProductFullDetail = props => {
469
499
  <div
470
500
  className={cn(
471
501
  classes.title,
472
- 'mb-[30px]',
502
+ 'mb-xs',
473
503
  )}
474
504
  >
475
505
  <h1
@@ -500,7 +530,7 @@ const ProductFullDetail = props => {
500
530
  <div
501
531
  className={cn(
502
532
  'product_short_description',
503
- 'mb-[30px]',
533
+ 'mb-xs',
504
534
  )}
505
535
  >
506
536
  {shortDescription}
@@ -514,7 +544,7 @@ const ProductFullDetail = props => {
514
544
  <div
515
545
  className={cn(
516
546
  'product_price_container',
517
- 'py-[30px] flex justify-between flex-wrap gap-y-5'
547
+ 'py-xs flex justify-between flex-wrap gap-y-5'
518
548
  )}
519
549
  >
520
550
  <div className='flex flex-col gap-y-5 max-w-[448px]'>
@@ -528,10 +558,11 @@ const ProductFullDetail = props => {
528
558
  >
529
559
  <Price
530
560
  currencyCode={productDetails.price.currency}
561
+ priceRange={productDetails.price_range}
531
562
  value={productDetails.price.value}
532
563
  />
533
564
  </p>
534
- <p
565
+ {/* <p
535
566
  data-cy="ProductFullDetail-productOldPrice"
536
567
  className={cn(
537
568
  classes.productPrice,
@@ -542,7 +573,7 @@ const ProductFullDetail = props => {
542
573
  currencyCode={productDetails.price.currency}
543
574
  value={productDetails.price.value}
544
575
  />
545
- </p>
576
+ </p> */}
546
577
  </div>
547
578
  <AuctionDetail className="auction_detail-container" />
548
579
  <PreorderDetail className={'preorder_detail-container'} />
@@ -564,7 +595,7 @@ const ProductFullDetail = props => {
564
595
  errors={errors.get('form') || []}
565
596
  />
566
597
  <section className={classes.options}>{options}</section>
567
- <section className={cn(classes.quantity, 'py-[30px] !border-none')}>
598
+ <section className={cn(classes.quantity, 'py-xs !border-none')}>
568
599
  {/* <span
569
600
  data-cy="ProductFullDetail-quantityTitle"
570
601
  className={classes.quantityTitle}
@@ -579,7 +610,7 @@ const ProductFullDetail = props => {
579
610
  message={errors.get('quantity')}
580
611
  />
581
612
 
582
- <div className='product_shipping-information mb-[30px] leading-[18px] mt-[25px]'>
613
+ {/* <div className='product_shipping-information mb-[30px] leading-[18px] mt-[25px]'>
583
614
  {sellerDetails &&
584
615
  <div className='text-xs'>
585
616
  Ship From <span className='font-medium'>{sellerDetails.country}</span>
@@ -590,48 +621,19 @@ const ProductFullDetail = props => {
590
621
  <div className='text-xs'>
591
622
  Shiping Method <span className='font-medium'>Store Pick Up | Meet Up</span>
592
623
  </div>
593
- </div>
624
+ </div> */}
594
625
 
595
626
  <div className='product_actions-wrapper'>
596
627
  {cartActionContent}
597
628
  </div>
598
629
  </section>
599
- <Divider />
630
+ {/* <Divider />
600
631
  <section className={cn(classes.quantity, 'py-[30px] !border-none')}>
601
632
  <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>
633
+ </section> */}
633
634
  <Divider />
634
- <section className='seller-information py-[30px]'>
635
+ <LinkToOtherStores productDetails={productDetails} />
636
+ <section className='seller-information py-xs'>
635
637
  <div className='flex xs_flex-col md_flex-row xs_items-center md_items-start items-start gap-[15px] relative'>
636
638
  <div className='flex flex-row justify-between relative w-full'>
637
639
  <div className='flex flex-col xs_items-center md_items-start gap-[6px] relative'>
@@ -644,7 +646,7 @@ const ProductFullDetail = props => {
644
646
  </div>
645
647
  </div>
646
648
  <div class="relative w-fit font-normal text-[#999999] text-[12px] tracking-[0] leading-[14px] whitespace-nowrap">
647
- {sellerDetails ? sellerDetails.city + ', ' + sellerDetails.country : ''}
649
+ {sellerDetails ? getSellerAddressDisplay(sellerDetails) : ''}
648
650
  </div>
649
651
  </div>
650
652
  <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;
@@ -0,0 +1,118 @@
1
+ import React from 'react';
2
+ import { bool, func, number, oneOfType, shape, string } from 'prop-types';
3
+ import { useIntl } from 'react-intl';
4
+ import { useStyle } from '@magento/venia-ui/lib/classify';
5
+ import defaultClasses from './tile.module.css';
6
+ import { useTile } from '@magento/peregrine/lib/talons/ProductOptions/useTile';
7
+ import Image from '@magento/venia-ui/lib/components/Image';
8
+ import resourceUrl from '@magento/peregrine/lib/util/makeUrl';
9
+
10
+ const getClassName = (
11
+ name,
12
+ isSelected,
13
+ hasFocus,
14
+ isOptionOutOfStock,
15
+ isEverythingOutOfStock
16
+ ) =>
17
+ `${name}${isSelected ? '_selected' : ''}${hasFocus ? '_focused' : ''}${
18
+ isEverythingOutOfStock || isOptionOutOfStock ? '_outOfStock' : ''
19
+ }`;
20
+
21
+ const Tile = props => {
22
+ const {
23
+ hasFocus,
24
+ isSelected,
25
+ item: { label, value_index },
26
+ onClick,
27
+ isEverythingOutOfStock,
28
+ isOptionOutOfStock,
29
+ setHoveredMedia,
30
+ itemVariant
31
+ } = props;
32
+
33
+ const talonProps = useTile({
34
+ onClick,
35
+ itemVariant,
36
+ setHoveredMedia,
37
+ value_index
38
+ });
39
+
40
+ const { handleMouseLeave, handleMouseEnter, handleClick, variantImg } = talonProps;
41
+ const classes = useStyle(defaultClasses, props.classes);
42
+ const className =
43
+ classes[
44
+ getClassName(
45
+ 'root',
46
+ isSelected,
47
+ hasFocus,
48
+ isOptionOutOfStock,
49
+ isEverythingOutOfStock
50
+ )
51
+ ];
52
+ const { formatMessage } = useIntl();
53
+ const ariaLabelView = formatMessage(
54
+ {
55
+ id: 'ProductOptions.productSize',
56
+ defaultMessage: 'Fashion size {label}'
57
+ },
58
+ { label: label }
59
+ );
60
+
61
+ const ariaLabelSelected = formatMessage(
62
+ {
63
+ id: 'productOptions.selectedSize',
64
+ defaultMessage: 'Fashion size {label} button Selected'
65
+ },
66
+ { label: label }
67
+ );
68
+
69
+ const result = isSelected ? ariaLabelSelected : ariaLabelView;
70
+
71
+ // variantImg
72
+ let image = (
73
+ <Image
74
+ alt=''
75
+ classes={{
76
+ root: classes.variantImageSwatch
77
+ }}
78
+ resource={variantImg}
79
+ width={24}
80
+ />
81
+ );
82
+
83
+ return (
84
+ <button
85
+ className={className}
86
+ onClick={handleClick}
87
+ onMouseEnter={handleMouseEnter}
88
+ onFocus={handleMouseEnter}
89
+ onTouchStart={handleMouseEnter}
90
+ onMouseLeave={handleMouseLeave}
91
+ title={label}
92
+ type="button"
93
+ data-cy="Tile-button"
94
+ aria-label={result}
95
+ disabled={isEverythingOutOfStock || isOptionOutOfStock}
96
+ >
97
+ {image}
98
+ <span className={classes.label}>{label}</span>
99
+ </button>
100
+ );
101
+ };
102
+
103
+ export default Tile;
104
+
105
+ Tile.propTypes = {
106
+ hasFocus: bool,
107
+ isSelected: bool,
108
+ item: shape({
109
+ label: string.isRequired,
110
+ value_index: oneOfType([number, string]).isRequired
111
+ }).isRequired,
112
+ onClick: func.isRequired
113
+ };
114
+
115
+ Tile.defaultProps = {
116
+ hasFocus: false,
117
+ isSelected: false
118
+ };