Skip to content

olegnn/react-shopping-cart

Repository files navigation

React shopping cart

npm Build Status npm peerDependencies Status dependencies devDependencies Status

Shopping cart package provides several components:

which can be used separately or in union. By default Redux is the framework to operate with data.

So, it's your choice to use Redux or not, but its reducers, actions and action types are already included.

Pay attention! All components are Pure.

Meta

Demo

Latest version demo (example1)

Usage

yarn add react-shopping-cart
npm i --save react-shopping-cart

Examples

In all cases you must include bootstrap version 4 (^alpha 0.6) in your project

import "bootstrap/dist/css/bootstrap.css";

And if you want to see animation, also include animate.css

import "animate.css/animate.min.css";

Also want some icons?

import "font-awesome/css/font-awesome.min.css";

With Redux. After store initialization you must dispatch setCartCurrency action or 'USD' will be used as cart's currency.

import React, { PureComponent } from "react";
import { Provider } from "react-redux";
import { createStore, combineReducers } from "redux";
import {
  Cart,
  Product,
  CheckoutButton,
  cartLocalization,
  cartReducer,
  setCartCurrency
} from "react-shopping-cart";

import "bootstrap/dist/css/bootstrap.css";
import "animate.css/animate.min.css";
import "font-awesome/css/font-awesome.min.css";

const { getDefaultLocalization } = cartLocalization;

// You may take localization object from wherever you want, that's just an example
// For more information, see localization section
const iPadCaseLocalization = {
  color: "Color",
  iPadCase: "iPad case",
  red: "Red",
  green: "Green",
  yellow: "Yellow",
  GBP: "ÂŁ",
  EUR: "€",
  USD: "$"
};

const iPadPropertiesWithAdditionalCostLocalization = {
  yellow: "Yellow (+{cost, number, CUR})"
};

const store = createStore(
  combineReducers({
    cart: cartReducer
    // Your own reducers, sir
  })
);

store.dispatch(setCartCurrency("USD"));

class App extends PureComponent {
  state = {
    product: {
      name: "iPadCase",
      id: "ipad-case",
      path: "/shop/ipad-case/",
      properties: {
        color: [
          "red",
          "green",
          {
            additionalCost: {
              GBP: 1,
              EUR: 2,
              USD: 3.5
            },
            value: "yellow"
          }
        ]
      },
      propertiesToShowInCart: ["color"],
      prices: { GBP: 70, EUR: 80, USD: 90 },
      currency: "GBP",
      imageSrc: "1-483x321.jpeg"
    },
    getProductLocalization: getDefaultLocalization("product", "en", {
      ...iPadCaseLocalization,
      ...iPadPropertiesWithAdditionalCostLocalization
    }),
    getCheckoutButtonLocalization: getDefaultLocalization(
      "checkoutButton",
      "en",
      iPadCaseLocalization
    ),
    getCartLocalization: getDefaultLocalization(
      "cart",
      "en",
      iPadCaseLocalization
    )
  };

  render() {
    const {
      product,
      getCheckoutButtonLocalization,
      getProductLocalization,
      getCartLocalization
    } = this.state;

    const checkoutButtonElement = (
      <CheckoutButton
        getLocalization={getCheckoutButtonLocalization}
        checkoutURL="/to/my/checkout"
      />
    );
    return (
      <Provider store={store}>
        <div className="container">
          <Product
            {...product}
            checkoutButton={checkoutButtonElement}
            getLocalization={getProductLocalization}
          />
          <Cart
            checkoutButton={checkoutButtonElement}
            getLocalization={getCartLocalization}
          />
        </div>
      </Provider>
    );
  }
}

export default App;
// You may also import actions and actionTypes

import { cartActions, cartActionTypes } from "react-shopping-cart";

// And do some cool things with them

Without redux

import React, { PureComponent } from "react";
import {
  CartComponent,
  ProductComponent,
  CheckoutButtonComponent,
  cartLocalization
} from "react-shopping-cart";

import "bootstrap/dist/css/bootstrap.css";
import "animate.css/animate.min.css";
import "font-awesome/css/font-awesome.min.css";

const { getDefaultLocalization } = cartLocalization;

// You may take localization object from wherever you want, that's just an example
// For more information, see localization section
const iPadCaseLocalization = {
  color: "Color",
  iPadCase: "iPad case",
  red: "Red",
  green: "Green",
  yellow: "Yellow",
  GBP: "ÂŁ",
  EUR: "€",
  USD: "$"
};

const iPadPropertiesWithAdditionalCostLocalization = {
  yellow: "Yellow (+{cost, number, CUR})"
};

class App extends PureComponent {
  state = {
    products: {},
    product: {
      name: "iPadCase",
      id: "ipad-case",
      path: "/shop/ipad-case/",
      properties: {
        color: [
          "red",
          "green",
          {
            additionalCost: {
              GBP: 1,
              EUR: 2,
              USD: 3.5
            },
            value: "yellow"
          }
        ]
      },
      propertiesToShowInCart: ["color"],
      prices: { GBP: 70, EUR: 80, USD: 90 },
      currency: "GBP",
      imageSrc: "1-483x321.jpeg"
    },
    getProductLocalization: getDefaultLocalization("product", "en", {
      ...iPadCaseLocalization,
      ...iPadPropertiesWithAdditionalCostLocalization
    }),
    getCheckoutButtonLocalization: getDefaultLocalization(
      "checkoutButton",
      "en",
      iPadCaseLocalization
    ),
    getCartLocalization: getDefaultLocalization(
      "cart",
      "en",
      iPadCaseLocalization
    )
  };

  addProduct = (key, product, currency) =>
    void this.setState(
      ({
        products: { [key]: cartProduct = { quantity: 0 }, ...restOfProducts }
      }) => ({
        products: {
          ...restOfProducts,
          [key]: {
            ...product,
            quantity: product.quantity + cartProduct.quantity
          }
        }
      })
    );

  generateProductKey = (id, properties) =>
    `${id}/${Object.entries(properties).join("_")}`;

  updateProduct = (key, updatedProduct) => void console.log(":)");

  removeProduct = key => void console.log(":C");

  render() {
    const {
      addProduct,
      generateProductKey,
      updateProduct,
      removeProduct,
      state
    } = this;

    const {
      getProductLocalization,
      getCheckoutButtonLocalization,
      getCartLocalization,
      products,
      product
    } = state;

    const checkoutButtonElement = (
      <CheckoutButtonComponent
        grandTotal={500}
        hidden={false}
        checkoutURL="/to/my/checkout"
        currency="GBP"
        getLocalization={getCheckoutButtonLocalization}
      />
    );
    return (
      <div className="container">
        <ProductComponent
          {...product}
          checkoutButton={checkoutButtonElement}
          onAddProduct={
            addProduct
            // Help product to get into the cart
          }
          generateProductKey={
            generateProductKey
            // create product key as you wish
          }
          getLocalization={getProductLocalization}
        />

        <CartComponent
          products={
            products
            // Provide your own product's Object(Look at Products)
          }
          onUpdateProduct={
            updateProduct
            // Update something
          }
          getLocalization={getCartLocalization}
          currency="GBP"
          onRemoveProduct={
            removeProduct
            // Remove something
          }
          checkoutButton={checkoutButtonElement}
          isCartEmpty={false}
          getLocalization={getCartLocalization}
        />
      </div>
    );
  }
}

export default App;

Localization

The default localization library is intl-messageformat. In order to localize your cart, you can chose one of the possible ways:

  • Create your own getLocalization function and pass it as prop to the cart's components
  • Create getLocalization function with bound localization using defaultLocalization object and getLocalization, getDefaultLocalization functions from cartLocalization module, pass it as prop to the cart's components
  • Don't do anything and see only default language in your cart :C

Generally, components require a function, which takes id and params(optional) and returns string, based on received arguments.

The first one should look like that if you're also using intl-messageformat:

  import React from 'react';
  import IntlMessageFormat from 'intl-messageformat';
  import { Cart } from 'react-shopping-cart';

  const localization = {
    en: {
      cart : {
        GBP: 'ÂŁ',
      },
    },
  };

  const getLocalization = (localizationPart, language, id, params = {}) =>  
    new IntlMessageFormat(localizationPart[id], language).format(params);

  <Cart
    getLocalization={(...args) => getLocalization(localization.en.cart, 'en', ...args)}
  />

Or you could use getDefaultLocalization function from cartLocalization:

  import React from 'react';
  import { Cart, cartLocalization } from 'react-shopping-cart';

  const { getDefaultLocalization } = cartLocalization;

  const localization = {
    GBP: 'ÂŁ',
    USD: '$',
  };

  <Cart
    getLocalization={getDefaultLocalization('cart', 'en', localization)}
  />

Example usage of getLocalization function from cartLocalization:

  import React from 'react';
  import { Cart, cartLocalization } from 'react-shopping-cart';

  const { getLocalization, defaultLocalization } = cartLocalization;

  const localization = {
    en: {
      cart : {
        GBP: 'ÂŁ',
      },
    },
  };

  const mergedEnCartLocalization = {
    ...localization.en.cart,
    ...defaultLocalization.en.cart,
  };

  <Cart
    getLocalization={(...args) => getLocalization(mergedEnCartLocalization, 'en', ...args)}
  />

For built-in getLocalization function you may write your translation for default statements as a string or object in format { component : Function | string, text : string, props? : object }. Because all components are pure, in order to relocalize your components, you should pass new getLocalization function, not old with changed scope.

Localization default ids and params bindings:

  • cart:

    • no params

      • shoppingCartTitle
    • { quantity, price, total, currency, name, localizedName, localizedCurrency, }

      • productName
      • quantityLabel
      • priceLabel
      • priceValue
      • totalLabel
      • totalValue
      • remove
      • your currency
      • your product's name
    • {name, value, localizedName, localizedValue,}

      • productPropertyLabel
      • productPropertyValue
      • your product's property name
      • your product's property value (if string of course)
  • checkoutButton

    • {currency, total, localizedCurrency,}
      • checkoutTotal
      • your currency
  • product

    • { name, quantity, price, currency, localizedName, localizedCurrency, }
      • price
      • quantityLabel
      • propertyLabel
      • addToCart
      • your product's name
      • your product's currency
    • { name, currency, localizedName, localizedCurrency, }
      • your product's name
      • your product's property name
      • {(optional) cost}
        • your product's property value (if string of course)

Table of Contents

Cart

Extends React.PureComponent

Component which represents shopping cart.

Meta

Props

Type: Object

Properties

  • products Products Products map. Required.
  • currency string Current currency. Required.
  • isCartEmpty boolean Display cart or not. Required.
  • onUpdateProduct UpdateProduct Function which will be called when product should be updated. First arg is product's key in products map, second - updated product. Required.
  • onRemoveProduct RemoveProduct Function to call when product should be removed from cart. One argument - product's key. Required.
  • getLocalization GetLocalization Required.
  • hideHeader boolean? Hide cart's header. Default is false.
  • checkoutButton ReactElement? Button to display in the bottom of cart. Default is null.
  • iconTrashClassName string? ClassName for trash icon on remove button. Default is 'fa fa-trash mx-1'.
  • altProductImageSrc string? Alt image src for products. Default is ''.
  • cartCSSTransition Object? Cart's config for react-transition-group/CSSTransition. Look at src/components/Cart/Cart.js for details.
  • cartItemCSSTransition Object? Cart item's config for react-transition-group/CSSTransition. Look at src/components/Cart/Cart.js for details.
  • linkComponent Link$Component? React Component, will receive prop to="%your product's page%". I'd recommend you to take a look at react-router's Link.

CartProduct

Extends React.PureComponent

React component to display product in cart.

Meta

ProductPropertyLabel

Extends React.PureComponent

React component to display product's property value in cart.

Meta

CheckoutButton

Extends React.PureComponent

Checkout button with grand total.

Meta

Props

Type: Object

Properties

  • grandTotal number Required.
  • currency string Current currency. Required.
  • hidden boolean Show or hide button. Required.
  • checkoutURL string Link to checkout page. Required.
  • getLocalization GetLocalization Required.
  • iconCheckoutClassName string? ClassName for cart icon on checkout button. Default is 'fa fa-shopping-cart mx-1'.
  • buttonCSSTransition Object? Transition's config for react-transition-group/CSSTransition.
  • linkComponent Link$Component? React Component, will receive prop to="%your product's page%". I'd recommend you to take a look at react-router's Link.

helpers

Meta

configure

Parameters

  • Component React$ComponentType<Props>
  • configuration Object

Returns React$ComponentType<Props>

isNaturalNumber

Parameters

Returns boolean

parseInteger

Parameters

Returns number

isObject

Parameters

  • value any

Returns boolean

getAbsoluteOffsetTop

Parameters

  • $0 HTMLElement (optional, default {})
    • $0.offsetTop
    • $0.offsetParent

Returns number

DefaultLinkComponent

Parameters

Returns React$Element<any>

fixInputValueStartingWithZero

Parameters

scrollFunction

Parameters

Product

Extends React.PureComponent

React component - Product form with price.

Meta

Props

Type: Object

Properties

  • id string Product's id. Required.
  • name string Name to display pattern. Required.
  • path string Path to product. Required.
  • prices Prices {currency: value}. Required
  • imageSrc string Path to main image. Required.
  • currency string Current price currency. Required.
  • onAddProduct AddProduct Function to call when user wants to add product in his cart. Required.
  • generateProductKey GenerateProductKey Required.
  • getLocalization GetLocalization Required.
  • properties ProductPropertiesOptions? Custom product properties. Each property option list consists of number, string or shape({ additionalCost(optional), onSelect(optional), value(required)})
  • propertiesToShowInCart Array<string>? Array of propery names to display in cart.
  • scrollAnimationConfig Object? Config for animateScroll (from react-scroll) scrollTo function.
  • scrollPosition ScrollPosition? Position to scroll after product add. May be number or function returning number.
  • scrollFunction ScrollFunction? Function which will be called when product has been added.
  • iconAddProductClassName string? ClassName for cart icon on add to button. Default is 'fa fa-cart-plus mx-1'.
  • checkoutButton ReactElement?
  • descriptionNode ReactNode? Node to display before price element.
  • afterPriceNode ReactNode? Node to display after price element.

ProductPropertiesOptions

Type: Object<string, PropertyOptions>

ScrollPosition

Type: (number | function (currentTarget: Element): number)

ScrollFunction

Type: function (currentTarget: EventTarget, scrollPosition: (number | function (currentTarget: Element): number), scrollAnimationConfig: Object): void

ProductPropertyInput

Extends React.PureComponent

React form for product property(options select only).

Meta

OptionIndex

Type: Object<string, number>

OptionObject

Type: Object

PropertyOption

Type: (ProductPropertyOption | OptionObject)

PropertyOptions

Type: Array<PropertyOption>

OnChange

Type: function (obj: {value: OptionIndex}): void

Shopping cart's data types

Meta

ProductPropertyOption

Type: (string | number)

ProductProperties

Type: Object<string, (string | number)>

Prices

Type: Object<string, number>

ProductData

Properties

Examples

{
   id: 'macbook-case',
   quantity: 3,
   properties: {
     color: 'red'
   },
   name: 'macbookCase',
   prices: {
    GBP: 50
   },
   path: '/shop/macbook-case/',
   imageSrc: '/shop/macbook-case/1-483x321.jpeg',
   propertiesToShowInCart: ['color']
 }

Products

Type: Object<string, ProductData>

GenerateProductKey

Type: function (id: string, properties: ProductProperties): string

AddProduct

Type: function (key: string, product: ProductData, currency: string): void

UpdateProduct

Type: function (key: string, updatedProduct: ProductData): void

RemoveProduct

Type: function (key: string): void

GetLocalization

Type: function (id: string, params: Object): (string | React$Element<any>)

CartAddAction

Properties

CartUpdateAction

Properties

CartRemoveAction

Properties

  • type "cart/REMOVE"
  • key string

CartEmptyAction

Properties

  • type "cart/EMPTY"

CartSetCurrencyAction

Properties

  • type "cart/SET_CURRENCY"
  • currency string

CartAction

Type: (CartAddAction | CartUpdateAction | CartRemoveAction | CartEmptyAction | CartSetCurrencyAction)

LocalizationPattern

Type: (string | {component: (string | React$ComponentType<any>), props: Object?, text: string})

Localization

Type: Object<string, LocalizationPattern>

MultiLocalization

Type: Object<string, Object<string, Localization>>

CartState

Properties

DefaultLinkComponentProps

Properties

Link$Component

Type: function (DefaultLinkComponentProps): React$Element<any>

Development

Developer mode

Run webpack-dev-server for example1

yarn start
npm run start

Build

yarn build
npm run build

And then check dist folder

Build Example

yarn build_example
npm run build_example

And then check examples folder

Testing

Jest is used for tests

yarn test
npm run test

Linter

ESLint is used as a linter

yarn lint
npm run lint

Formatter

prettier-eslint is used as a formatter

yarn fmt
npm run fmt

Flow Type

Check types in project using Flow

yarn flow
npm run flow

Autodoc

Generate doc using documentation js

yarn doc
npm run doc

And then look at README.md