Skip to content

Commit

Permalink
cart quantity action
Browse files Browse the repository at this point in the history
  • Loading branch information
Oksydan committed Sep 24, 2023
1 parent 0789af6 commit dda40eb
Show file tree
Hide file tree
Showing 19 changed files with 377 additions and 136 deletions.
5 changes: 4 additions & 1 deletion _dev/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ module.exports = {
},
extends: ['airbnb-base'],
rules: {
'max-len': ['error', {code: 140}],
'max-len': ['error', {
code: 140,
ignoreComments: true,
}],
'no-underscore-dangle': 'off',
'no-restricted-syntax': 'off',
'no-param-reassign': 'off'
Expand Down
14 changes: 8 additions & 6 deletions _dev/js/theme/components/http/useHttpPayloadDefinition.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ const useHttpPayloadDefinition = (payload, definition) => {
payloadDefinition[fieldName] = setDefaultDefinitionForField(definitionForField);
});

const getValueType = (value) => typeof value;

const validate = (fieldName, value, fieldDefinition) => {
const validateErrors = [];
const {
Expand All @@ -62,32 +64,32 @@ const useHttpPayloadDefinition = (payload, definition) => {
switch (type) {
case 'string':
if (typeof value !== 'string') {
validateErrors.push(`'${fieldName}' ${ERROR_MESSAGES.TYPE} string`);
validateErrors.push(`'${fieldName}' ${ERROR_MESSAGES.TYPE} string, ${getValueType(value)} received`);
}
break;
case 'float':
if (typeof value !== 'number') {
validateErrors.push(`'${fieldName}' ${ERROR_MESSAGES.TYPE} float`);
validateErrors.push(`'${fieldName}' ${ERROR_MESSAGES.TYPE} float, ${getValueType(value)} received`);
}
break;
case 'int':
if (typeof value !== 'number') {
validateErrors.push(`'${fieldName}' ${ERROR_MESSAGES.TYPE} int`);
validateErrors.push(`'${fieldName}' ${ERROR_MESSAGES.TYPE} int, ${getValueType(value)} received`);
}
break;
case 'boolean':
if (typeof value !== 'boolean') {
validateErrors.push(`'${fieldName}' ${ERROR_MESSAGES.TYPE} boolean`);
validateErrors.push(`'${fieldName}' ${ERROR_MESSAGES.TYPE} boolean, ${getValueType(value)} received`);
}
break;
case 'object':
if (typeof value !== 'object') {
validateErrors.push(`'${fieldName}' ${ERROR_MESSAGES.TYPE} object`);
validateErrors.push(`'${fieldName}' ${ERROR_MESSAGES.TYPE} object, ${getValueType(value)} received`);
}
break;
case 'array':
if (!Array.isArray(value)) {
validateErrors.push(`'${fieldName}' ${ERROR_MESSAGES.TYPE} array`);
validateErrors.push(`'${fieldName}' ${ERROR_MESSAGES.TYPE} array, ${getValueType(value)} received`);
}
break;
default:
Expand Down
166 changes: 162 additions & 4 deletions _dev/js/theme/components/useCustomQuantityInput.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
/**
* Custom quantity input
* @module useCustomQuantityInput
* @param {HTMLElement} spinnerElement - spinner element to initialize (required)
* @param {object} configuration - configuration object (optional)
* @param {string} configuration.spinnerInitializedClass - class to add when spinner is initialized (default: js-custom-qty-spinner-initialized)
* @param {string} configuration.spinnerInputClass - class of the input element (default: js-custom-qty-spinner-input)
* @param {string} configuration.spinnerBtnClassUp - class of the up button (default: js-custom-qty-btn-up)
* @param {string} configuration.spinnerBtnClassDown - class of the down button (default: js-custom-qty-btn-down)
* @param {int} configuration.defaultMinQty - default minimum quantity (default: 1)
* @param {int} configuration.defaultMaxQty - default maximum quantity (default: 1000000)
* @param {int} configuration.timeout - timeout in ms to wait before dispatching change event (default: 500)
* @param {function} configuration.onQuantityChange - callback function to call when quantity changes
*/
const useCustomQuantityInput = (spinnerElement, {
spinnerInitializedClass = 'js-custom-qty-spinner-initialized',
spinnerInputClass = 'js-custom-qty-spinner-input',
Expand All @@ -8,13 +22,52 @@ const useCustomQuantityInput = (spinnerElement, {
timeout = 500,
onQuantityChange = () => {},
}) => {
/**
* Timeout id
* @private
* @type {int|null}
*/
let timeoutId = null;
/**
* Start value
* @private
* @type {int|null}
*/
let startValue = null;
/**
* Minimum quantity
* @private
* @type {int|null}
*/
let minQty = null;

/**
* Maximum quantity
* @private
* @type {int|null}
*/
let maxQty = null;

/**
* Current quantity
* @private
* @type {int|null}
*/
let currentQty = null;

/**
* DOM elements
* @private
* @type {Object|null}
*/
let DOMElements = null;

/**
* Set DOM elements
* @method setDOMElements
* @private
* @returns {{input: HTMLElement | undefined, btnUp: HTMLElement | undefined, btnDown: HTMLElement | undefined}}
*/
const setDOMElements = () => {
const elements = {
input: spinnerElement?.querySelector(`.${spinnerInputClass}`),
Expand All @@ -27,12 +80,30 @@ const useCustomQuantityInput = (spinnerElement, {
return elements;
};

/**
* Get DOM elements
* @method getDOMElements
* @public
* @returns {{input: (HTMLElement|undefined), btnUp: (HTMLElement|undefined), btnDown: (HTMLElement|undefined)}}
*/
const getDOMElements = () => (DOMElements || setDOMElements());

/**
* Reset DOM elements
* @method resetDomElements
* @private
* @returns {void}
*/
const resetDomElements = () => {
DOMElements = null;
};

/**
* Set initial value
* @method setInitialValue
* @private
* @returns {void}
*/
const setInitialValue = () => {
const { input } = getDOMElements();
startValue = input.value ? parseInt(input.value, 10) : defaultMinQty;
Expand All @@ -41,8 +112,20 @@ const useCustomQuantityInput = (spinnerElement, {
maxQty = input.getAttribute('max') ? parseInt(input.getAttribute('max'), 10) : defaultMaxQty;
};

/**
* Should dispatch change event
* @method shouldDispatchChange
* @private
* @returns {boolean}
*/
const shouldDispatchChange = () => currentQty !== startValue;

/**
* Get operation type
* @method getOperationType
* @private
* @returns {string} increase|decrease
*/
const getOperationType = () => {
if (currentQty > startValue) {
return 'increase';
Expand All @@ -51,8 +134,20 @@ const useCustomQuantityInput = (spinnerElement, {
return 'decrease';
};

/**
* Get quantity difference
* @method getQtyDifference
* @private
* @returns {int}
*/
const getQtyDifference = () => Math.abs(currentQty - startValue);

/**
* Dispatch change event
* @method dispatchChange
* @private
* @returns {void}
*/
const dispatchChange = () => {
clearTimeout(timeoutId);

Expand All @@ -72,6 +167,13 @@ const useCustomQuantityInput = (spinnerElement, {
}, timeout);
};

/**
* Set quantity
* @param {int} qty - quantity to set
* @method setQty
* @private
* @returns {void}
*/
const setQty = (qty) => {
const { input } = getDOMElements();

Expand All @@ -81,8 +183,15 @@ const useCustomQuantityInput = (spinnerElement, {
dispatchChange();
};

const handleClickUp = (e) => {
e.preventDefault();
/**
* Handle click up
* @param {Event} event - event object
* @method handleClickUp
* @private
* @returns {void}
*/
const handleClickUp = (event) => {
event.preventDefault();

let newQty = parseInt(currentQty, 10) + 1;

Expand All @@ -93,8 +202,15 @@ const useCustomQuantityInput = (spinnerElement, {
setQty(newQty);
};

const handleClickDown = (e) => {
e.preventDefault();
/**
* Handle click down
* @param {Event} event - event object
* @method handleClickDown
* @private
* @returns {void}
*/
const handleClickDown = (event) => {
event.preventDefault();

let newQty = parseInt(currentQty, 10) - 1;

Expand All @@ -105,6 +221,12 @@ const useCustomQuantityInput = (spinnerElement, {
setQty(newQty);
};

/**
* Handle input change
* @method handleInputChange
* @private
* @returns {void}
*/
const handleInputChange = () => {
const { input } = getDOMElements();
let newQty = parseInt(input.value, 10);
Expand All @@ -120,14 +242,32 @@ const useCustomQuantityInput = (spinnerElement, {
setQty(newQty);
};

/**
* Add initialized class
* @method setInitializedClass
* @private
* @returns {void}
*/
const setInitializedClass = () => {
spinnerElement?.classList.add(spinnerInitializedClass);
};

/**
* Remove initialized class
* @method removeInitializedClass
* @private
* @returns {void}
*/
const removeInitializedClass = () => {
spinnerElement?.classList.remove(spinnerInitializedClass);
};

/**
* Detach events from DOM elements
* @method detachEvents
* @private
* @returns {void}
*/
const detachEvents = () => {
const { input, btnUp, btnDown } = getDOMElements();

Expand All @@ -139,6 +279,12 @@ const useCustomQuantityInput = (spinnerElement, {
input?.removeEventListener('blur', handleInputChange);
};

/**
* Attach events to DOM elements
* @method attachEvents
* @private
* @returns {void}
*/
const attachEvents = () => {
const { input, btnUp, btnDown } = getDOMElements();

Expand All @@ -150,11 +296,23 @@ const useCustomQuantityInput = (spinnerElement, {
input?.addEventListener('blur', handleInputChange);
};

/**
* Destroy spinner instance and detach events
* @method destroy
* @static
* @returns {void}
*/
const destroy = () => {
detachEvents();
resetDomElements();
};

/**
* Initialize spinner instance and attach events
* @method init
* @static
* @returns {void}
*/
const init = () => {
destroy();
setInitialValue();
Expand Down
24 changes: 24 additions & 0 deletions _dev/js/theme/core/cart/cartController.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import codeLinkSubmitHandler from './handler/voucher/codeLinkSubmitHandler';
import deleteVoucherHandler from './handler/voucher/deleteVoucherHandler';
import addToCartHandler from './handler/cart/addToCartHandler';
import deleteFromCartHandler from './handler/cart/deleteFromCartHandler';
import quantityChangeHandler from './handler/cart/quantityChangeHandler';
import useCustomQuantityInput from '../../components/useCustomQuantityInput';

const { on } = useEvent();

Expand All @@ -14,6 +16,22 @@ const { on } = useEvent();
* @returns {function} return.init initialize cart controller
*/
const cartController = () => {
const attachSpinnerEvents = () => {
const spinners = document.querySelectorAll('.js-custom-cart-qty-spinner');

spinners.forEach((spinner) => {
const { init, getDOMElements } = useCustomQuantityInput(spinner, {
onQuantityChange: ({ operation, qtyDifference }) => {
const { input } = getDOMElements();

quantityChangeHandler(operation, qtyDifference, input);
},
});

init();
});
};

const init = () => {
const {
discountCode,
Expand All @@ -24,6 +42,12 @@ const cartController = () => {
on(document, 'click', '.js-voucher-delete', deleteVoucherHandler);
on(document, 'click', '[data-button-action="add-to-cart"]', addToCartHandler);
on(document, 'click', '.js-remove-from-cart', deleteFromCartHandler);

attachSpinnerEvents();

prestashop.on('updatedCart', () => {
attachSpinnerEvents();
});
};

return {
Expand Down
Loading

0 comments on commit dda40eb

Please sign in to comment.