Skip to content

Commit

Permalink
Removes ability to add configurable items to cart until options are c…
Browse files Browse the repository at this point in the history
…hosen (#1097)

* Disables Add to Cart button until configurable products have all options chosen

* Updates logic to determine if a product is configurable.

- Is now based on the product __typename
- No longer checks if configurable_items is an array

* Updates configurable item fixture to fix broken tests
  • Loading branch information
supernova-at authored and dpatil-magento committed Apr 4, 2019
1 parent 9afa103 commit ddcd58a
Show file tree
Hide file tree
Showing 9 changed files with 70 additions and 27 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
query productDetail($urlKey: String, $onServer: Boolean!) {
productDetail: products(filter: { url_key: { eq: $urlKey } }) {
items {
__typename
sku
name
price {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Object {
"query": "query productDetail($urlKey: String, $onServer: Boolean!) {
productDetail: products(filter: { url_key: { eq: $urlKey } }) {
items {
__typename
sku
name
price {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const cartItem = {
};

const configItem = {
__typename: 'ConfigurableProduct',
configurable_options: [
{
attribute_code: 'size',
Expand Down
17 changes: 7 additions & 10 deletions packages/venia-concept/src/components/MiniCart/cartOptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import defaultClasses from './cartOptions.css';
import Button from 'src/components/Button';
import Quantity from 'src/components/ProductQuantity';
import appendOptionsToPayload from 'src/util/appendOptionsToPayload';
import isProductConfigurable from 'src/util/isProductConfigurable';

// TODO: get real currencyCode for cartItem
const currencyCode = 'USD';
Expand Down Expand Up @@ -36,6 +37,7 @@ class CartOptions extends Component {
qty: number.isRequired
}),
configItem: shape({
__typename: string,
configurable_options: array
}),
isUpdatingItem: bool,
Expand Down Expand Up @@ -69,39 +71,34 @@ class CartOptions extends Component {
handleClick = async () => {
const { updateCart, cartItem, configItem } = this.props;
const { optionSelections, quantity } = this.state;
const { configurable_options } = configItem;
const isConfigurable = Array.isArray(configurable_options);
const productType = isConfigurable
? 'ConfigurableProduct'
: 'SimpleProduct';

const payload = {
item: configItem,
productType,
productType: configItem.__typename,
quantity: quantity
};

if (productType === 'ConfigurableProduct') {
if (isProductConfigurable(configItem)) {
appendOptionsToPayload(payload, optionSelections);
}

updateCart(payload, cartItem.item_id);
};

render() {
const { fallback, handleSelectionChange, props } = this;
const { classes, cartItem, configItem, isUpdatingItem } = props;
const { name, price } = cartItem;
const { configurable_options } = configItem;

const modalClass = isUpdatingItem
? classes.modal_active
: classes.modal;

const options = Array.isArray(configurable_options) ? (
const options = isProductConfigurable(configItem) ? (
<Suspense fallback={fallback}>
<section className={classes.options}>
<Options
options={configurable_options}
options={configItem.configurable_options}
onSelectionChange={handleSelectionChange}
/>
</section>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import RichText from 'src/components/RichText';
import defaultClasses from './productFullDetail.css';
import appendOptionsToPayload from 'src/util/appendOptionsToPayload';
import findMatchingVariant from 'src/util/findMatchingProductVariant';
import isProductConfigurable from 'src/util/isProductConfigurable';

const Options = React.lazy(() => import('../ProductOptions'));

Expand All @@ -35,6 +36,7 @@ class ProductFullDetail extends Component {
title: string
}),
product: shape({
__typename: string,
id: number,
sku: string.isRequired,
price: shape({
Expand Down Expand Up @@ -63,7 +65,7 @@ class ProductFullDetail extends Component {
const optionCodes = new Map(state.optionCodes);

// if this is a simple product, do nothing
if (!Array.isArray(configurable_options)) {
if (!isProductConfigurable(props.product)) {
return null;
}

Expand All @@ -87,19 +89,14 @@ class ProductFullDetail extends Component {
const { props, state } = this;
const { optionSelections, quantity, optionCodes } = state;
const { addToCart, product } = props;
const { configurable_options } = product;
const isConfigurable = Array.isArray(configurable_options);
const productType = isConfigurable
? 'ConfigurableProduct'
: 'SimpleProduct';

const payload = {
item: product,
productType,
productType: product.__typename,
quantity
};

if (productType === 'ConfigurableProduct') {
if (isProductConfigurable(product)) {
appendOptionsToPayload(payload, optionSelections, optionCodes);
}

Expand All @@ -122,7 +119,7 @@ class ProductFullDetail extends Component {
get productOptions() {
const { fallback, handleSelectionChange, props } = this;
const { configurable_options } = props.product;
const isConfigurable = Array.isArray(configurable_options);
const isConfigurable = isProductConfigurable(props.product);

if (!isConfigurable) {
return null;
Expand All @@ -142,13 +139,9 @@ class ProductFullDetail extends Component {
const { props, state } = this;
const { product } = props;
const { optionCodes, optionSelections } = state;
const {
configurable_options,
media_gallery_entries,
variants
} = product;
const { media_gallery_entries, variants } = product;

const isConfigurable = Array.isArray(configurable_options);
const isConfigurable = isProductConfigurable(product);

if (
!isConfigurable ||
Expand All @@ -170,8 +163,31 @@ class ProductFullDetail extends Component {
return item.product.media_gallery_entries;
}

get isMissingOptions() {
const { product } = this.props;

// Non-configurable products can't be missing options
if (!isProductConfigurable(product)) {
return false;
}

// Configurable products are missing options if we have fewer
// option selections than the product has options.
const { configurable_options } = product;
const numProductOptions = configurable_options.length;
const numProductSelections = this.state.optionSelections.size;

return numProductSelections < numProductOptions;
}

render() {
const { addToCart, productOptions, props, mediaGalleryEntries } = this;
const {
addToCart,
isMissingOptions,
mediaGalleryEntries,
productOptions,
props
} = this;
const { classes, isAddingItem, product } = props;
const { regularPrice } = product.price;

Expand Down Expand Up @@ -212,7 +228,7 @@ class ProductFullDetail extends Component {
<Button
priority="high"
onClick={addToCart}
disabled={isAddingItem}
disabled={isAddingItem || isMissingOptions}
>
<span>Add to Cart</span>
</Button>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
query productDetail($urlKey: String, $onServer: Boolean!) {
productDetail: products(filter: { url_key: { eq: $urlKey } }) {
items {
__typename
sku
name
price {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
query productDetailByName($name: String, $onServer: Boolean!) {
products(filter: { name: { eq: $name } }) {
items {
__typename
id
sku
name
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import isProductConfigurable from '../isProductConfigurable';

test('returns true for a configurable product', () => {
const product = {
__typename: 'ConfigurableProduct'
};

const result = isProductConfigurable(product);

expect(result).toBe(true);
});

test('returns false for a non-configurable product', () => {
const product = {
__typename: 'SimpleProduct'
};

const result = isProductConfigurable(product);

expect(result).toBe(false);
});
4 changes: 4 additions & 0 deletions packages/venia-concept/src/util/isProductConfigurable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
const isProductConfigurable = product =>
product.__typename === 'ConfigurableProduct';

export default isProductConfigurable;

1 comment on commit ddcd58a

@vercel
Copy link

@vercel vercel bot commented on ddcd58a Apr 4, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully aliased the URL https://magento-venia-ocifdlagdp.now.sh to the following aliases.

Please sign in to comment.