SmartWishlist
How to integrate SmartWishlist
Integration Step-By-Step
First of all you need to create a getWishList function with one argument - setList.This argument is used for updating state in the layouts/Search component. Then you need to define an array of the wishlist items (wishListArray) and pass it to setList.
const getWishList = ( setList ) =>{
// there should be a code for getting an array of wishListArray
setList(wishListArray);
}
export default getWishList;
After that you need to import getWishList to layouts/Search component and call the function on initial render. Inside of the layouts/Search component you also need to create a state for your wishList and addToWishListArray function.
/** code */
const [wishList, setList] = useState([])
// updating wishList on initial render
useEffect(() => {
const timeout = setTimeout(() => {
getWishList(setList)
}, 500)
return () => {
clearTimeout(timeout);
}}
, []);
// function for updating wishList on click
const addToWishListArray = (id, selectedId) => {
const isInWishList = wishList && wishList.find(i => {
return i.indexOf(id) > -1
})
setList(wishList => {
return isInWishList ? wishList.filter(i => i.indexOf(id) == -1) : [...wishList, [id, selectedId, 1]]
})
}
The final result of the above implementation may look like this:
import { useEffect } from 'react';
import StaticResults from 'components/search/StaticResults';
import LazyResults from 'components/search/LazyResults';
import DesktopFacets from 'components/search/DesktopFacets';
import MobileActions from 'components/search/MobileActions';
import DesktopActions from 'components/search/DesktopActions';
import Branch from 'components/common/Branch';
import Banner from 'components/Banner';
import { List } from 'immutable';
import Grid, { Column } from 'components/common/Grid';
import { useMobile } from 'helpers/useMobile';
import { useAnnouncement } from 'components/common/Announcement';
import useScrollOnChange from 'helpers/useScrollOnChange';
import { useItems } from '@findify/react-connect';
import useTranslations from 'helpers/useTranslations';
import styles from 'layouts/Search/styles.css';
import { Immutable } from '@findify/store-configuration';
import { ThemedSFCProps, IProduct } from 'types';
import getWishList from "getWishList";
/** Props that search layout accepts */
export interface ISearchProps extends ThemedSFCProps<typeof styles> {
isCollection?: boolean;
/** Items list */
items: List<IProduct>;
}
const Search = ({ isCollection, theme = styles }) => {
const { items, config } = useItems<Immutable.SearchConfig>();
const translate = useTranslations();
const isMobile = useMobile();
const [announcement, setAnnouncement] = useAnnouncement();
//create state for wishList
const [wishList, setList] = useState([])
useScrollOnChange(items);
useEffect(() => setAnnouncement(translate('search.accessibleUpdate')), [
items,
]);
useEffect(() => {
const timeout = setTimeout(() => {
//getting wishList
getWishList(setList)
}, 500)
return () => {
clearTimeout(timeout);
}}
, []);
const addToWishListArray = (id) => {
const isInWishList = wishList && wishList.indexOf(id) > -1;
setList(wishList => {
return isInWishList ? wishList.filter(i => i !== id) : [...wishList, id]
})
}
if (!items.size) return null;
return (
<>
<Grid
className={theme.root}
gutter={40}
columns={
config.getIn(['facets', 'position']) === 'top'
? 'full'
: config.getIn(['breakpoints', 'layout'], 'fit|auto')
}
>
<Column
display-if={!isMobile}
order={config.getIn(['facets', 'position']) === 'right' && 2}
>
<DesktopFacets />
</Column>
<>
<Branch
isCollection={isCollection}
condition={isMobile}
left={MobileActions}
right={DesktopActions}
/>
<Banner />
<Branch
condition={config.getIn(['pagination', 'type']) === 'lazy'}
left={LazyResults}
right={StaticResults}
wishList={wishList}
addToWishListArray={addToWishListArray}
/>
</>
</Grid>
{announcement}
</>
);
};
export default process.env.HOT
? require('react-hot-loader').hot(module)(Search)
: Search;
After that, you need to create a new wishlist component, i.e. WishList.tsx. You need to check if the current product is already in the wishlist, and utilize addToSmartWishList/removeFromSmartWishList function to update the states accordingly.
import React from "react";
import cx from "classnames";
const WishListTootlip = ({ className, isInList }) => {
const text = isInList ? "Remove from" : "Add to";
return(
<div className={className} onClick={e => e.stopPropagation()}>
<div className="findify-wishlist-tootlip__content">
{text} <a href="/a/wishlist">Wishlist</a>
</div>
<div className="findify-wishlist-tootlip__arrow">
<span className="findify-wishlist-tootlip__arrow-border"></span>
<span style={{borderColor: "rgb(167, 156, 157)"}}></span>
</div>
</div>
)
}
const WishList = ({ sustainable, wishList, item, addToWishListArray }) => {
const id = item.get('id');
const selectedId = item.get('selected_variant_id');
const isInWishList = wishList && wishList.find(i => {
return i.indexOf(id) > -1
})
const onClick = (e) => {
e.stopPropagation();
if(isInWishList){
RemoveFromSmartWishlist(item.get('id'), item.get('selected_variant_id'));
}
else {
AddToSmartWishlist(item.get('id'), item.get('selected_variant_id'));
}
addToWishListArray(id, selectedId)
}
return (
<span
className="findify-wishlist"
data-wishlist-btn=""
data-product={item.get('id')}
aria-label="Add to Wishlist"
role="button"
tabindex="0"
>
<div onClick={onClick} className="findify-wishlist__wrapper">
// use your Icons here
</div>
</span>
)
}
export default WishList;
Finally, you can use custom WishList.tsx in the components/Cards/Product component:
import cx from 'classnames';
import Image from 'components/common/Image';
import Rating from 'components/Cards/Product/Rating';
import Price from 'components/Cards/Product/Price';
import Title from 'components/Cards/Product/Title';
import Description from 'components/Cards/Product/Description';
import Variants from 'components/Cards/Product/Variants';
import styles from 'components/Cards/Product/styles.css';
import {
DiscountSticker,
OutOfStockSticker,
} from 'components/Cards/Product/Stickers';
import { List } from 'immutable';
import { IProduct, ThemedSFCProps } from 'types';
import { Immutable, Product } from '@findify/store-configuration';
import trackProductPosition from 'helpers/trackProductPosition';
import { useMemo, useState } from 'react';
export interface IProductCardProps extends ThemedSFCProps {
item: IProduct;
config: Immutable.Factory<Product>;
Container?: React.ElementType;
highlighted: boolean;
isAutocomplete?: boolean;
}
const useVariants = (
item
): [IProduct, React.Dispatch<React.SetStateAction<string>>] => {
const [currentVariant, setVariant] = useState<string>(
item.get('selected_variant_id')
);
const variant = useMemo(
() =>
item.merge(
item.get('variants')?.find((i) => i.get('id') === currentVariant)
),
[currentVariant]
);
return [variant, setVariant];
};
export default ({
item,
theme = styles,
className,
config,
Container = 'div',
highlighted,
isAutocomplete,
wishList,
addToWishListArray
}: IProductCardProps) => {
const container = trackProductPosition(item);
const [variant, setVariant] = useVariants(item);
return (
<Container
ref={container}
data-element="card"
className={cx(
theme.root,
theme[config.get('template')],
highlighted && theme.highlighted,
isAutocomplete && theme.autocomplete,
className
)}
>
<div className={theme.content}>
<Rating
className={theme.rating}
value={variant.getIn(['reviews', 'average_score'])}
count={
variant.getIn(['reviews', 'count']) ||
variant.getIn(['reviews', 'total_reviews'])
}
display-if={
!!variant.getIn(['reviews', 'count']) ||
!!variant.getIn(['reviews', 'total_reviews'])
}
/>
<Variants config={config} item={item} />
{/*
Link hack:
Title's "a" contains :after element with absolute position
what makes provide link effect to the rest of card
- To remove element from the effect set `position:relative`
- Or `z-index: 1`, but it may have side effects
*/}
<Title
display-if={!!variant.get('title')}
theme={theme}
onClick={variant.onClick}
href={variant.get('product_url')}
text={variant.get('title')}
/>
<Description
display-if={!!variant.get('description')}
theme={theme}
text={variant.get('description')}
/>
<div className={theme.divider} />
<Price
display-if={!!variant.get('price')}
className={theme.priceWrapper}
item={item}
/>
<OutOfStockSticker
display-if={variant.getIn(['stickers', 'out-of-stock'])}
config={config}
/>
</div>
{/*
ADA specific hack:
We need to make image belong to content, so we move it under the title.
- flex order set to -1
*/}
<div className={theme.image} onClick={item.onClick}>
<Image
aspectRatio={config.getIn(['image', 'aspectRatio'])}
thumbnail={variant.get('thumbnail_url')}
alt={variant.get('title')}
lazy={config.getIn(['image', 'lazy'])}
offset={config.getIn(['image', 'lazyOffset'])}
src={
config.getIn(['image', 'multiple'])
? [variant.get('image_url'), variant.get('image_2_url')]
: variant.get('image_url') || variant.get('thumbnail_url')
}
/>
<WishList
wishList={wishList}
item={item}
display-if={!isAutocomplete}
addToWishListArray={addToWishListArray}
/>
<DiscountSticker
config={config}
className={theme.discountSticker}
discount={variant.get('discount')}
display-if={
config.getIn(['stickers', 'discount']) &&
variant.get('discount', List()).size &&
variant.getIn(['stickers', 'discount'])
}
/>
</div>
</Container>
);
};
Updated about 1 year ago