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 wil 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 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 = variant;
newVariant.meta = defaultItem.meta;
newVariant.analytics = defaultItem.analytics;
return {
currentVariant: newVariant,
defaultItem
}
}
}
),
withHandlers(({ item }) => ({
//This function will be the new 'click on product' function, that will also send analytics info
onClick: ({ item }) => (e, currentVariant) =>{
e.preventDefault();
const openInNewWindow = e && (e.ctrlKey || e.metaKey);
currentVariant.analytics.sendEvent(
'click-item',
{ rid: currentVariant.meta.get('rid'), item_id: item.get('id'), variant_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 = variant;
newVariant.meta = defaultItem.meta;
newVariant.analytics = defaultItem.analytics;
return {
currentVariant: newVariant,
defaultItem
}
}
}
),
withHandlers(({ item }) => ({
//This function will be the new 'click on product' function, that will also send analytics info
onClick: ({ item }) => (e, currentVariant) =>{
e.preventDefault();
const openInNewWindow = e && (e.ctrlKey || e.metaKey);
currentVariant.analytics.sendEvent(
'click-item',
{ rid: currentVariant.meta.get('rid'), item_id: item.get('id'), variant_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);
Updated over 4 years ago