Skip to content
This repository has been archived by the owner on Jul 14, 2022. It is now read-only.

Commit

Permalink
Merge pull request #194 from mirumee/cart_quantity_controls
Browse files Browse the repository at this point in the history
Add ability to change quantity in the cart
  • Loading branch information
maarcingebala authored Jan 9, 2019
2 parents 064af7e + 646af0e commit 64af588
Show file tree
Hide file tree
Showing 46 changed files with 696 additions and 435 deletions.
3 changes: 3 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ jobs:
- run:
name: Install dependencies
command: npm install
- run:
name: Run tslint
command: npm run tslint
- run:
name: Build application
command: npm run build
Expand Down
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Add add to cart indicator #173 by @piotrgrundas
Fix product page tablet view #181 by @piotrgrundas
Add collection view, fix cursor pagination for categories, update storefront to use new thumbnail structure #178 by @piotrgrundas
Storefront UX improvements, remove signup to newsletter #182 by @piotrgrundas
Fix two line titles breaking fatured carousel, product page improvements #184 by @piotrgrundas
Fix two line titles breaking featured carousel, product page improvements #184 by @piotrgrundas
Allow numbers in product, category & collection urls #185 by @piotrgrundas
Add OpenGraph and Meta tags, minor UI improvements #191 by @piotrgrundas
Add tslint check in the CI, ability to change cart quantity, fix error after removing item from the cart #194 by @piotrgrundas
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@
"watch": "webpack -d --watch",
"storybook": "start-storybook -p 9001 -c src/storybook/ -s src/",
"codegen": "apollo codegen:generate --target=typescript types",
"codegen-watch": "npm run codegen -- --watch"
"codegen-watch": "npm run codegen -- --watch",
"tslint": "tslint 'src/**/*.ts?(x)'"
}
}
2 changes: 1 addition & 1 deletion src/components/App/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ import * as React from "react";

import { ApolloConsumer } from "react-apollo";
import {
CartOverlay,
Footer,
MainMenu,
MainMenuNavOverlay,
MetaConsumer,
MobileNav,
SearchOverlay
} from "..";
import { CartOverlay } from "../CartOverlay";
import CartProvider from "../CartProvider";
import { LoginOverlay } from "../LoginOverlay";
import { NotificationOverlay } from "../NotificationOverlay";
Expand Down
3 changes: 2 additions & 1 deletion src/components/App/routes.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import * as React from "react";
import { Route, Switch } from "react-router-dom";

import { CartPage, CheckoutLogin } from "..";
import { CheckoutLogin } from "..";
import { ArticlePage } from "../../views/Article";
import { CartPage } from "../../views/Cart";
import { CategoryPage } from "../../views/Category";
import { CollectionPage } from "../../views/Collection";
import { HomePage } from "../../views/Home";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as React from "react";
interface CachedImageProps {
url: string;
url2x?: string;
alt?: string;
}

interface CachedImageState {
Expand Down Expand Up @@ -56,12 +57,16 @@ class CachedImage extends React.Component<CachedImageProps, CachedImageState> {
}

render() {
const { url, url2x } = this.props;
const { url, url2x, alt } = this.props;
if (this.state.isUnavailable) {
return this.props.children || null;
}
return (
<img src={url} srcSet={url2x ? `${url} 1x, ${url2x} 2x` : `${url} 1x`} />
<img
src={url}
srcSet={url2x ? `${url} 1x, ${url2x} 2x` : `${url} 1x`}
alt={alt}
/>
);
}
}
Expand Down
32 changes: 32 additions & 0 deletions src/components/CachedImage/CachedThumbnail.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import * as React from "react";

import { maybe } from "../../core/utils";
import CachedImage from "./CachedImage";

const noPhotoPng = require("../../images/nophoto.png");

const CachedThumbnail: React.SFC<{
source: {
thumbnail: { url: string; alt: string };
thumbnail2x: { url: string };
};
noPhotoDefault?: boolean;
children?: React.ReactNode;
}> = ({ source, noPhotoDefault, children }) => {
const defaultImg = noPhotoDefault ? noPhotoPng : undefined;
return (
<CachedImage
url={maybe(() => source.thumbnail.url, defaultImg)}
url2x={maybe(() => source.thumbnail2x.url, defaultImg)}
alt={maybe(() => source.thumbnail.alt, "")}
>
{children}
</CachedImage>
);
};

CachedThumbnail.defaultProps = {
noPhotoDefault: true
};

export default CachedThumbnail;
2 changes: 2 additions & 0 deletions src/components/CachedImage/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { default as CachedImage } from "./CachedImage";
export { default as CachedThumbnail } from "./CachedThumbnail";
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,27 @@ import { Link } from "react-router-dom";
import ReactSVG from "react-svg";

import { Button } from "..";
import { maybe, priceToString } from "../../core/utils";
import { priceToString } from "../../core/utils";
import { checkoutLoginUrl } from "../App/routes";
import { CartContext } from "../CartProvider/context";
import { Error } from "../Error";
import GoToCart from "../GoToCart";
import { GoToCheckout } from "../GoToCheckout";
import Loader from "../Loader";
import Offline from "../Offline";
import OfflinePlaceholder from "../OfflinePlaceholder";
import Online from "../Online";
import { Overlay } from "../Overlay";
import { OverlayContext, OverlayType } from "../Overlay/context";
import { ShopContext } from "../ShopProvider/context";
import { UserContext } from "../User/context";

import CachedImage from "../CachedImage";
import Offline from "../Offline";
import OfflinePlaceholder from "../OfflinePlaceholder";
import Online from "../Online";
import Empty from "./Empty";
import ProductList from "./ProductList";

const cartSvg = require("../../images/cart.svg");
const closeSvg = require("../../images/x.svg");
const noPhotoPng = require("../../images/nophoto.png");
const removeSvg = require("../../images/garbage.svg");

export const CartOverlay: React.SFC = () => (
const CartOverlay: React.SFC = () => (
<OverlayContext.Consumer>
{overlay =>
overlay.type === OverlayType.cart ? (
Expand All @@ -46,11 +44,13 @@ export const CartOverlay: React.SFC = () => (
</div>
);
}

if (errors) {
return errors.map(error => (
<Error error={error.message} />
));
}

return (
<div className="cart">
<div className="overlay__header">
Expand All @@ -66,45 +66,16 @@ export const CartOverlay: React.SFC = () => (
</div>
<ReactSVG
path={closeSvg}
onClick={() => overlay.hide()}
onClick={overlay.hide}
className="overlay__header__close-icon"
/>
</div>
{lines.length ? (
<>
<ul className="cart__list">
{lines.map(line => (
<li
key={line.variant.id}
className="cart__list__item"
>
<CachedImage
url={maybe(
() => line.variant.product.thumbnail.url,
noPhotoPng
)}
url2x={maybe(
() => line.variant.product.thumbnail2x.url
)}
/>
<div className="cart__list__item__details">
<p>{line.variant.price.localized}</p>
<p>{line.variant.product.name}</p>
<span className="cart__list__item__details__variant">
<span>{line.variant.name}</span>
<span>{"Qty: " + line.quantity}</span>
</span>
<ReactSVG
path={removeSvg}
className="cart__list__item__details__delete-icon"
onClick={() =>
cart.remove(line.variant.id)
}
/>
</div>
</li>
))}
</ul>
<ProductList
lines={lines}
removeFromCart={cart.remove}
/>
<div className="cart__footer">
<div className="cart__footer__subtotoal">
<span>Subtotal</span>
Expand Down Expand Up @@ -152,18 +123,7 @@ export const CartOverlay: React.SFC = () => (
</div>
</>
) : (
<div className="cart__empty">
<h4>Yor bag is empty</h4>
<p>
You haven’t added anything to your bag. We’re sure
you’ll find something in our store
</p>
<div className="cart__empty__action">
<Button secondary onClick={() => overlay.hide()}>
Continue Shopping
</Button>
</div>
</div>
<Empty overlayHide={overlay.hide} />
)}
</div>
);
Expand All @@ -182,3 +142,5 @@ export const CartOverlay: React.SFC = () => (
}
</OverlayContext.Consumer>
);

export default CartOverlay;
20 changes: 20 additions & 0 deletions src/components/CartOverlay/Empty.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import * as React from "react";

import { Button } from "..";

const Empty: React.SFC<{ overlayHide(): void }> = ({ overlayHide }) => (
<div className="cart__empty">
<h4>Yor bag is empty</h4>
<p>
You haven’t added anything to your bag. We’re sure you’ll find something
in our store
</p>
<div className="cart__empty__action">
<Button secondary onClick={overlayHide}>
Continue Shopping
</Button>
</div>
</div>
);

export default Empty;
47 changes: 47 additions & 0 deletions src/components/CartOverlay/ProductList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import * as React from "react";
import { Link } from "react-router-dom";
import ReactSVG from "react-svg";

import { CachedThumbnail } from "..";
import { generateProductUrl } from "../../core/utils";
import { CartLineInterface } from "../CartProvider/context";

const removeSvg = require("../../images/garbage.svg");

const ProductList: React.SFC<{
lines: CartLineInterface[];
removeFromCart(variantId: string): void;
}> = ({ lines, removeFromCart }) => (
<ul className="cart__list">
{lines.map(line => {
const productUrl = generateProductUrl(
line.variant.product.id,
line.variant.product.name
);
return (
<li key={line.variant.id} className="cart__list__item">
<Link to={productUrl}>
<CachedThumbnail source={line.variant.product} />
</Link>
<div className="cart__list__item__details">
<p>{line.variant.price.localized}</p>
<Link to={productUrl}>
<p>{line.variant.product.name}</p>
</Link>
<span className="cart__list__item__details__variant">
<span>{line.variant.name}</span>
<span>{`Qty: ${line.quantity}`}</span>
</span>
<ReactSVG
path={removeSvg}
className="cart__list__item__details__delete-icon"
onClick={() => removeFromCart(line.variant.id)}
/>
</div>
</li>
);
})}
</ul>
);

export default ProductList;
1 change: 1 addition & 0 deletions src/components/CartOverlay/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as CartOverlay } from "./CartOverlay";
Loading

0 comments on commit 64af588

Please sign in to comment.