SmartWishlist

How to integrate SmartWishlist

661

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>
  );
};