Skip to content

Commit

Permalink
Merge pull request Shopify#50 from Shopify/qty-errors
Browse files Browse the repository at this point in the history
Nicer quantity error handling
  • Loading branch information
cshold committed Apr 28, 2014
2 parents 13090ae + daae79b commit c54edb9
Show file tree
Hide file tree
Showing 6 changed files with 185 additions and 67 deletions.
130 changes: 100 additions & 30 deletions assets/ajaxify.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// (c) Copyright 2013 Shopify Inc. Author: Carson Shold (@cshold). All Rights Reserved.
// (c) Copyright 2014 Shopify Inc. Author: Carson Shold (@cshold). All Rights Reserved.

/*
* Ajaxify the add to cart experience and flip the button for inline confirmation,
Expand Down Expand Up @@ -31,7 +31,8 @@
*/


// JQUERY API (c) Copyright 2009 Jaded Pixel. Author: Caroline Schnapp. All Rights Reserved. Includes slight modifications to addItemFromForm
// JQUERY API (c) Copyright 2009-2014 Shopify Inc. Author: Caroline Schnapp. All Rights Reserved.
// Includes slight modifications to addItemFromForm.

if ((typeof Shopify) === 'undefined') { Shopify = {}; }

Expand Down Expand Up @@ -262,7 +263,7 @@ var ajaxifyShopify = (function(module, $) {
var $formContainer, $btnClass, $wrapperClass, $addToCart, $flipClose, $flipCart, $flipContainer, $cartCountSelector, $cartCostSelector, $toggleCartButton, $modal, $cartContainer, $drawerCaret, $modalContainer, $modalOverlay, $closeCart, $drawerContainer;

// Private functions
var updateCountPrice, flipSetup, revertFlipButton, modalSetup, showModal, hideModal, drawerSetup, showDrawer, hideDrawer, sizeDrawer, formOverride, itemAddedCallback, itemErrorCallback, cartUpdateCallback, setToggleButtons, flipCartUpdateCallback, buildCart, cartTemplate, adjustCart, adjustCartCallback, createQtySelectors, scrollTop, isEmpty, log;
var updateCountPrice, flipSetup, revertFlipButton, modalSetup, showModal, hideModal, drawerSetup, showDrawer, hideDrawer, sizeDrawer, formOverride, itemAddedCallback, itemErrorCallback, cartUpdateCallback, setToggleButtons, flipCartUpdateCallback, buildCart, cartTemplate, adjustCart, adjustCartCallback, createQtySelectors, qtySelectors, scrollTop, isEmpty, log;

/**
* Initialise the plugin and define global options
Expand All @@ -281,7 +282,9 @@ var ajaxifyShopify = (function(module, $) {
btnClass: null,
wrapperClass: null,
useCartTemplate: false,
moneyFormat: '${{amount}}'
moneyFormat: '${{amount}}',
disableAjaxCart: false,
enableQtySelectors: true
};

// Override defaults with arguments
Expand Down Expand Up @@ -312,25 +315,32 @@ var ajaxifyShopify = (function(module, $) {
$('body').addClass('ajaxify-touch');
}

// Handle each case add to cart method
switch (settings.method) {
// Setup ajax quantity selectors on the any template if enableQtySelectors is true
if (settings.enableQtySelectors) {
qtySelectors();
}

case 'flip':
flipSetup();
break;
// Enable the ajax cart
if (!settings.disableAjaxCart) {
// Handle each case add to cart method
switch (settings.method) {
case 'flip':
flipSetup();
break;

case 'modal':
modalSetup();
break;
case 'modal':
modalSetup();
break;

case 'drawer':
drawerSetup();
break;
}
case 'drawer':
drawerSetup();
break;
}

if ( $addToCart.length ) {
// Take over the add to cart form submit
formOverride();
if ( $addToCart.length ) {
// Take over the add to cart form submit
formOverride();
}
}

// Run this function in case we're using the quantity selector outside of the cart
Expand Down Expand Up @@ -525,6 +535,9 @@ var ajaxifyShopify = (function(module, $) {
$formContainer.submit(function(e) {
e.preventDefault();

// Remove any previous quantity errors
$('.qty-error').remove();

Shopify.addItemFromForm(e.target, itemAddedCallback, itemErrorCallback);

// Set the flip button to a loading state
Expand Down Expand Up @@ -556,9 +569,12 @@ var ajaxifyShopify = (function(module, $) {
break;
}

// This is where you handle errors of products being added to the cart.
// Default to alert message for errors.
Shopify.onError(XMLHttpRequest, textStatus);
var data = eval('(' + XMLHttpRequest.responseText + ')');
if (!!data.message) {
if (data.status == 422) {
$formContainer.after('<div class="errors qty-error">'+ data.description +'</div>')
}
}
};

cartUpdateCallback = function (cart) {
Expand Down Expand Up @@ -750,7 +766,7 @@ var ajaxifyShopify = (function(module, $) {
adjustCart = function () {
// This function runs on load, and when the cart is reprinted

// Create ajax friendly quantity fields and remove links
// Create ajax friendly quantity fields and remove links in the ajax cart
if (settings.useCartTemplate) {
createQtySelectors();
}
Expand Down Expand Up @@ -832,14 +848,15 @@ var ajaxifyShopify = (function(module, $) {
});

function updateQuantity(id, qty) {
// This function only adds activity classes if using the default handlebar.js templates.
// The item quantity will be updated normally if using the /cart template.
// Add activity classes when changing cart quantities
if (!settings.useCartTemplate) {
var row = $('.ajaxifyCart--row[data-id="' + id + '"]').parent().addClass('ajaxifyCart--is-loading');
var row = $('.ajaxifyCart--row[data-id="' + id + '"]').addClass('ajaxifyCart--is-loading');
} else {
var row = $('.cart-row[data-id="' + id + '"]').addClass('ajaxifyCart--is-loading');
}

if ( qty == 0 ) {
row.addClass('is-removed');
}
if ( qty == 0 ) {
row.addClass('is-removed');
}

// Slight delay to make sure removed animation is done
Expand Down Expand Up @@ -914,7 +931,60 @@ var ajaxifyShopify = (function(module, $) {
var el = $(this).addClass('ajaxifyCart--remove');
});
}
},
};

qtySelectors = function() {
// Change number inputs to JS ones, similar to ajax cart but without API integration.
// Make sure to add the existing ID to the new input element
var numInputs = $('input[type="number"]');

if (numInputs.length) {
numInputs.each(function() {
var el = $(this),
currentQty = el.val(),
inputName = el.attr('name');

// Ignore inputs without a data-id
if (!el.attr('data-id')) return false;

var itemAdd = currentQty + 1,
itemMinus = currentQty - 1,
itemQty = currentQty;

var source = $("#jsQty").html(),
template = Handlebars.compile(source),
data = {
id: el.data('id'),
itemQty: itemQty,
itemAdd: itemAdd,
itemMinus: itemMinus,
inputName: inputName
};

// Append new quantity selector then remove original
el.after(template(data)).hide();
});

// Setup listeners to add/subtract from the input
$('.js--qty-adjuster').on('click', function() {
var el = $(this),
id = el.data('id'),
qtySelector = el.siblings('.js--num'),
qty = parseInt( qtySelector.val() );

// Add or subtract from the current quantity
if (el.hasClass('js--add')) {
qty = qty + 1;
} else {
qty = qty - 1;
if (qty <= 1) qty = 1;
}

// Update the input's number
qtySelector.val(qty);
});
}
};

scrollTop = function () {
if ($('body').scrollTop() > 0) {
Expand Down
100 changes: 67 additions & 33 deletions assets/ajaxify.scss.liquid
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@ $drawerBackgroundColor: #222;
$drawerTextColor: #fff;
$drawerLinkColor: #fff;

/* Border colors from Timber */
$borderColor: #e5e5e5;

$quantityTextColor: #fff;

.ajaxify-grey {
Expand Down Expand Up @@ -148,7 +151,6 @@ form[action^="/cart/add"] {
display: block;
float: left;
position: relative;
margin-bottom: 1em;
@include perspective(600px);
}

Expand Down Expand Up @@ -388,64 +390,96 @@ form[action^="/cart/add"] {
color: $ajaxifyGrey;
}

.ajaxifyCart--qty {

// ==============================================================================
// #Quantity Selectors
// ==============================================================================
.js-qty {
position: relative;
top: -5px;
right: -30px;
border: 1px solid transparent;
border-radius: 2px;
overflow: hidden;
font-size: 16px;
max-width: 90px;
margin-bottom: 1em;
@include transition(all 0.4s ease-out);
@include backface();

.ajaxifyCart--row & {
top: 0;
}

.ajaxifyCart--product:hover &,
.ajaxify-touch &,
&:hover {
border-color: $ajaxifyGreyDark;
@include transition(none);
}

.ajaxifyCart--is-loading & {
opacity: 0.7;
border-color: $ajaxifyGreyDark;
@include transition(none);
}

input[type="text"] {
display: block;
border: 0 none;
border-radius: 0;
@include box-shadow(none);
background: none;
color: $quantityTextColor;
font-size: 1em;
text-align: center;
width: 100%;
padding: 5px 25px;
margin: 0;
@include box-shadow(none);
}
}

.ajaxifyCart--qty-adjuster {
.js--qty-adjuster {
cursor: pointer;
position: absolute;
display: block;
opacity: 0;
top: 0;
bottom: 0;
padding: 5px 0;
width: 20px;
text-align: center;
color: $ajaxifyGrey;
overflow: hidden;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
-o-user-select: none;
user-select: none;
@include transition(all 0.2s ease-out);

&:hover {
background-color: $borderColor;
}
}

.js--add {
right: 0;
border-left: 1px solid $borderColor;
}

.js--minus {
left: 0;
border-right: 1px solid $borderColor;
}

// ==============================================================================
// #Quantity Selectors in the Ajax Cart
// ==============================================================================
.ajaxifyCart--qty {
@extend .js-qty;
top: -5px;
right: -30px;
margin-bottom: 0;
overflow: hidden;
max-width: 90px;

.ajaxifyCart--product:hover &,
.ajaxify-touch &,
&:hover {
border-color: $ajaxifyGreyDark;
@include transition(none);
}

input[type="text"] {
color: $quantityTextColor;
border: 0 none;
}

.ajaxifyCart--is-loading & {
opacity: 0.6;
border-color: $ajaxifyGreyDark;
@include transition(none);
}
}

.ajaxifyCart--qty-adjuster {
@extend .js--qty-adjuster;
opacity: 0;

.ajaxifyCart--product:hover &,
.ajaxify-touch &,
.ajaxifyCart--qty:hover & {
Expand All @@ -469,12 +503,12 @@ form[action^="/cart/add"] {
}

.ajaxifyCart--add {
right: 0;
@extend .js--add;
border-left: 1px solid $ajaxifyGreyDark;
}

.ajaxifyCart--minus {
left: 0;
@extend .js--minus;
border-right: 1px solid $ajaxifyGreyDark;
}

Expand Down
6 changes: 4 additions & 2 deletions layout/theme.liquid
Original file line number Diff line number Diff line change
Expand Up @@ -159,9 +159,11 @@
cartCountSelector: '#cartCount', // Update number of items in the cart when a product is added. Default is null.
cartCostSelector: '#cartCost', // Update the total cart cost when a product is added. Default is null.
toggleCartButton: '#cartToggle', // To toggle drawer/modal cart, include the selector here. Default is null and will take you to /cart page.
useCartTemplate: true, // True to use cart.liquid markup. Default is false and uses handlebars.js for template.
useCartTemplate: true, // True to use cart.liquid markup. False to use handlebars.js for template. Default is true.
btnClass: 'btn', // Your main button class for styling purposes if useCartTemplate is set to false. Default is null.
moneyFormat: {{ shop.money_format | json }} // Your shop money format, defined in liquid.
moneyFormat: {{ shop.money_format | json }}, // Your shop money format, defined in liquid.
disableAjaxCart: false, // If for some reason you want to disable the cart, set to true. Default is false.
enableQtySelectors: true // Enable the quantity selectors on your templates, even if disableAjaxCart is false. Default is true.
});
});
</script>
Expand Down
9 changes: 9 additions & 0 deletions snippets/ajax-cart-template.liquid
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,13 @@
<span class="ajaxifyCart--qty-adjuster ajaxifyCart--minus" data-id="{{id}}" data-qty="{{itemMinus}}">-</span>
</div>
{% endraw %}
</script>
<script id="jsQty" type="text/template">
{% raw %}
<div class="js-qty">
<input type="text" class="js--num" value="{{itemQty}}" min="1" data-id="{{id}}" aria-label="quantity" pattern="[0-9]*" name="{{inputName}}">
<span class="js--qty-adjuster js--add" data-id="{{id}}" data-qty="{{itemAdd}}">+</span>
<span class="js--qty-adjuster js--minus" data-id="{{id}}" data-qty="{{itemMinus}}">-</span>
</div>
{% endraw %}
</script>
Loading

0 comments on commit c54edb9

Please sign in to comment.