commercetools-8.x-1.2-alpha1/modules/commercetools_decoupled/js/molecules/AddToCartForm.js
modules/commercetools_decoupled/js/molecules/AddToCartForm.js
class AddToCartForm extends HTMLElement {
connectedCallback() {
const currentVariant = this.product.currentSku
? this.getCurrentVariant({
sku: this.product.currentSku,
})
: this.product.masterVariant;
const form = document.createElement('form');
form.classList.add('add-to-cart-form');
const attributes = this.getEnabledProductAttributes();
// Sort attributes: attributes with more than one option come last
attributes.sort((attr) => {
return Object.keys(attr.options).length > 1 ? 1 : -1;
});
if (attributes.length) {
const container = document.createElement('div');
container.classList.add(
'attributes',
'border-top',
'border-bottom',
'pt-4',
'pb-4',
'mt-4',
'mb-4',
);
attributes.forEach((attr) => {
const attrContainer = document.createElement('div');
attrContainer.classList.add(
'd-flex',
'justify-content-between',
'w-100',
);
const label = document.createElement('span');
label.classList.add('fw-medium', 'align-self-center');
label.innerText = attr.label;
attrContainer.append(label);
const optionKeys = Object.keys(attr.options);
if (optionKeys.length <= 1) {
const value = document.createElement('span');
const eventData = {
key: attr.name,
labelValue: attr.options[optionKeys[0]],
};
document.dispatchEvent(
new CustomEvent('commercetools:productAttributeLabel', {
detail: eventData,
}),
);
value[
attr.name === 'color' || attr.name === 'finish'
? 'innerHTML'
: 'innerText'
] = eventData.labelValue || '-';
attrContainer.classList.add('pt-1', 'pb-1');
attrContainer.append(value);
} else {
const select = document.createElement('select');
select.classList.add('form-select', 'w-50');
select.name = attr.name;
optionKeys.forEach((optionKey) => {
const option = document.createElement('option');
option.value = optionKey;
option.textContent = attr.options[optionKey];
option.selected =
optionKey === currentVariant.attributes[attr.name].value;
select.append(option);
});
attrContainer.append(select);
attrContainer.classList.add('form-item');
}
container.append(attrContainer);
});
form.append(container);
}
const addToCart = document.createElement('input');
addToCart.classList.add(
'btn-lg',
'button',
'js-form-submit',
'form-submit',
'btn',
'btn-primary',
);
addToCart.value = Drupal.t('Add to Cart');
addToCart.type = 'submit';
form.append(addToCart);
// Update the product page and change the current sku according to args.
form.addEventListener('change', () => {
const formData = new FormData(form);
const attrArgs = Object.fromEntries(formData.entries());
const newVariant = this.getCurrentVariant({ attributes: attrArgs });
this.product.currentSku = newVariant.sku;
this?.handleSkuChange(newVariant.sku);
const url = Drupal.url(
`${drupalSettings.path.currentPath}?sku=${newVariant.sku}`,
);
window.history.pushState({ path: url }, '', url);
});
form.addEventListener('submit', async (e) => {
e.preventDefault();
const response = await window.commercetools.updateCart({
actions: [{ addLineItem: { quantity: 1, sku: currentVariant.sku } }],
});
document.dispatchEvent(
new CustomEvent('commercetoolsDecoupledCartUpdated', {
detail: { totalItems: response.lineItems.length },
}),
);
if (window.drupalSettings.commercetoolsDecoupled.cartPath) {
const cartUrl = Drupal.url(
window.drupalSettings.commercetoolsDecoupled.cartPath.substring(1),
);
new Drupal.Message().add(
Drupal.t(
`${this.product.name} added to <a href="${cartUrl}">cart</a>`,
),
);
} else {
new Drupal.Message().add(
Drupal.t('The cart page path is not configured.'),
{
type: 'error',
},
);
}
});
this.appendChild(form);
}
getEnabledProductAttributes() {
if (this.product.isLoading) {
return [];
}
const variants = [this.product.masterVariant, ...this.product.variants];
const productType = this.product.productType.key;
const attributes =
drupalSettings.commercetoolsDecoupled.attributes[productType] || [];
return attributes.map((attr) => {
const options = {};
variants.forEach((variant) => {
if (!variant.attributes?.[attr.name]) {
return;
}
const { value, labelValue } = variant.attributes[attr.name];
options[value] = labelValue;
});
return {
name: attr.name,
label: attr.label,
options,
};
});
}
getCurrentVariant(variantArgs) {
const variant = this.product.variants.find((v) => {
if (variantArgs.sku && variantArgs.sku !== v.sku) {
return false;
}
if (variantArgs.attributes) {
return Object.keys(variantArgs.attributes).every((attrName) => {
return (
v.attributes[attrName].value === variantArgs.attributes[attrName]
);
});
}
return true;
});
return variant || this.product.masterVariant;
}
}
customElements.define('ct-add-to-cart-form', AddToCartForm);
