Redirect to certain variant with analytics

📘

Components:

This implementation is needed whenever you want to make sure that somehow selected variant on the search results/collection pages will redirect to this particular variant on the product page (i.e. if you have color swatches or some variant selector in the results).
To do that, we first need to create a function for navigation and for getting the product handle:

const navigate = (openInNewWindow, url) => {
  if (!window) return;
  if (openInNewWindow) return window.open(url, '_blank');
  return window.location.href = url;
}

const getProductHandle = (url) =>{
  var url_title = url.split('products/')[1];
  var url = url_title.slice(0, url_title.indexOf('?'))
  return url;
}

Then we will create current variant state handler and new onClick function:

const enhancer = compose(
  withStateHandlers(({ item }) => ({
      currentVariant: item, //this will be the current selected variant
      defaultItem: item  //default item, to help passing proper props to the new variant item
    }),
    {
        //This function will take the selected variant as the argument, and will create the current variant with proper props for the analytics
      setCurrentVariant: ({ currentVariant, defaultItem }) => (variant) => {
        let newVariant =  currentVariant.set('product_url', variant.get('product_url')).set('image_url', variant.get('image_url'));
        newVariant.meta = defaultItem.meta;
        newVariant.analytics = defaultItem.analytics;
        return {
          currentVariant: newVariant,
          defaultItem
        }
      }
    }
  ),
  withHandlers({
    //This function will be the new 'click on product' function, that will also send analytics info 
    onClick: () => (e, currentVariant) =>{
      e.preventDefault();
      const openInNewWindow = e && (e.ctrlKey || e.metaKey);
      currentVariant.analytics.sendEvent(
        'click-item',
        { rid: currentVariant.meta.get('rid'), item_id: currentVariant.get('id') },
        !openInNewWindow
      );
      navigate(openInNewWindow, currentVariant.get('product_url'));
    }
  })
);

So the final ProductCard view component may look like this:

/**
 * @module components/Cards/Product
 */
//... 
import { withState, withProps, compose, withStateHandlers, withHandlers } from 'recompose';

//..Some code

const navigate = (openInNewWindow, url) => {
  if (!window) return;
  if (openInNewWindow) return window.open(url, '_blank');
  return window.location.href = url;
}

const getProductHandle = (url) =>{
  var url_title = url.split('products/')[1];
  var url = url_title.slice(0, url_title.indexOf('?'))
  return url;
}


const enhancer = compose(
  withStateHandlers(({ item }) => ({
      currentVariant: item, //this will be the current selected variant
      defaultItem: item  //default item, to help passing proper props to the new variant item
    }),
    {
        //This function will take the selected variant as the argument, and will create the current variant with proper props for the analytics
      setCurrentVariant: ({ currentVariant, defaultItem }) => (variant) => {
        let newVariant =  currentVariant.set('product_url', variant.get('product_url')).set('image_url', variant.get('image_url'));
        newVariant.meta = defaultItem.meta;
        newVariant.analytics = defaultItem.analytics;
        return {
          currentVariant: newVariant,
          defaultItem
        }
      }
    }
  ),
  withHandlers({
    //This function will be the new 'click on product' function, that will also send analytics info 
    onClick: () => (e, currentVariant) =>{
      e.preventDefault();
      const openInNewWindow = e && (e.ctrlKey || e.metaKey);
      currentVariant.analytics.sendEvent(
        'click-item',
        { rid: currentVariant.meta.get('rid'), item_id: currentVariant.get('id') },
        !openInNewWindow
      );
      navigate(openInNewWindow, currentVariant.get('product_url'));
    }
  })
);

/..Some more code



const ProductCardView: React.SFC<IProductCardProps> = ({
  item,
  defaultItem,
  config,
  theme,
  currentVariant,
  setCurrentVariant,
  onClick
}: any) =>
  <div>
    <a
      onClick={(e) => onClick(e, currentVariant)}
      href={currentVariant.get('product_url')}
      className={classNames(
        theme.root,
        config.get('simple') && theme.simple,
        theme.productCard,
      )}  
    >
      <div className={classNames(theme.imageWrap)}>
        <Image
          className={classNames(theme.image)}
          aspectRatio={config.getIn(['product', 'image', 'aspectRatio'], 1)}
          thumbnail={currentVariant.get('thumbnail_url')}
          src={currentVariant.get('image_url') || currentVariant.get('thumbnail_url')}
          alt={currentVariant.get('title')}
        />
        <OutOfStockSticker
          display-if={swatchAvailability(currentVariant.getIn(['stickers', 'swatch_outofstock']), currentVariant)}
          config={config} />
        <div display-if={config.getIn(['product', 'stickers', 'display'])}>
          <DiscountSticker
            config={config}
            className={theme.discountSticker}
            discount={item.get('discount')}
            display-if={
              ((item.get('discount') && typeof item.get('discount').size !== 'undefined')
              ? item.get('discount').size > 0
              : item.get('discount')) && item.getIn(['stickers', 'discount'])
            } />
        </div>
      </div>
    </a>
     //This will be the color swatches component
     //That will change the current variant
    <ColorSwatches 
      display-if={item.getIn(['custom_fields', 'colors'])}
      variantColors={item.getIn(['custom_fields', 'colors']).reverse()}
      variants={item.get('variants')}
      setVariant={setCurrentVariant} 
      item={item}
      isMobile={isMobile}
      currentSwatch={getProductHandle(currentVariant.get('product_url'))}
    />
 
    <div display-if={config.getIn(['product', 'reviews', 'display']) && !!item.getIn(['reviews', 'count'])} className={theme.rating}>
      <Rating value={item.getIn(['reviews', 'average_score'])} count={item.getIn(['reviews', 'count'])} />
    </div>
    
    <div className={theme.content}>
      <Title
        theme={theme}
        display-if={config.getIn(['product', 'title', 'display'])}
        text={item.get('title').slice(0, item.get('title').indexOf('|'))}
        config={config.getIn(['product', 'title'])} />
      <Description
        theme={theme}
        display-if={config.getIn(['product', 'description', 'display'])}
        text={item.get('description')}
        config={config.getIn(['product', 'description'])} />
      <Price
        className={theme.priceWrapper}
        display-if={config.getIn(['product', 'price', 'display'])}
        price={item.get('price')}
        oldPrice={item.get('compare_at')}
        range={item.toJS().variants.map(i => i.compare_at)}
        discount={item.get('discount')}
        currency={config.get('currency').toJS()} />
    </div>
  </div>


//dont forget to put enhancer HOC
export default enhancer(ProductCardView);