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.
getWishList,tsx
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.
layouts/Search
/** code */const [wishList, setList] = useState([])// updating wishList on initial renderuseEffect(() => { const timeout = setTimeout(() => { getWishList(setList) }, 500) return () => { clearTimeout(timeout); }}, []);// function for updating wishList on clickconst 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:
layouts/Search
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.
WishList.tsx
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:
components/Cards/Product
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> );};