Skip to content

Commit

Permalink
Updated option value picker to use product_option_value.variant, upda…
Browse files Browse the repository at this point in the history
…te availability handling to rely on async variant pagination

Removed old event handler, update buy-buttons to handle selected_variant undefined case

Fix for cases where selected_or_first_available_variant is null

Only pass variant if present
  • Loading branch information
lhoffbeck committed Jan 29, 2024
1 parent b700fc9 commit 8f6fe02
Show file tree
Hide file tree
Showing 14 changed files with 379 additions and 255 deletions.
1 change: 1 addition & 0 deletions assets/base.css
Original file line number Diff line number Diff line change
Expand Up @@ -3308,6 +3308,7 @@ details-disclosure > details {
opacity: 1;
animation: none;
transition: none;
transform: none;
}

.scroll-trigger.scroll-trigger--design-mode.animate--slide-in {
Expand Down
5 changes: 5 additions & 0 deletions assets/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,9 @@ const PUB_SUB_EVENTS = {
quantityUpdate: 'quantity-update',
variantChange: 'variant-change',
cartError: 'cart-error',
sectionRefreshed: 'section-refreshed',
};

const SECTION_REFRESH_RESOURCE_TYPE = {
product: 'product',
};
445 changes: 270 additions & 175 deletions assets/global.js

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions assets/pickup-availability.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ if (!customElements.get('pickup-availability')) {
}

fetchAvailability(variantId) {
if (!variantId) return;

let rootUrl = this.dataset.rootUrl;
if (!rootUrl.endsWith('/')) {
rootUrl = rootUrl + '/';
Expand Down
2 changes: 2 additions & 0 deletions assets/quick-add.css
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
.quick-add-modal .scroll-trigger.scroll-trigger {
animation: none;
opacity: 1;
transform: none;
}

.quick-add-modal__content {
Expand Down Expand Up @@ -170,6 +171,7 @@ quick-add-modal .product__column-sticky {
}

quick-add-modal .product:not(.product--no-media) .product__info-wrapper {
padding-top: 2rem;
padding-left: 4rem;
max-width: 54%;
width: calc(54% - var(--grid-desktop-horizontal-spacing) / 2);
Expand Down
74 changes: 38 additions & 36 deletions assets/quick-add.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,83 +25,85 @@ if (!customElements.get('quick-add-modal')) {
.then((response) => response.text())
.then((responseText) => {
const responseHTML = new DOMParser().parseFromString(responseText, 'text/html');
this.productElement = responseHTML.querySelector('section[id^="MainProduct-"]');
this.productElement.classList.forEach((classApplied) => {
if (classApplied.startsWith('color-') || classApplied === 'gradient')
this.modalContent.classList.add(classApplied);
});
this.preventDuplicatedIDs();
this.removeDOMElements();
this.setInnerHTML(this.modalContent, this.productElement.innerHTML);
const productElement = responseHTML.querySelector('section[id^="MainProduct-"]');

this.preprocessHTML(productElement);
HTMLUpdateUtility.setInnerHTML(this.modalContent, productElement.innerHTML);

if (window.Shopify && Shopify.PaymentButton) {
Shopify.PaymentButton.init();
}

if (window.ProductModel) window.ProductModel.loadShopifyXR();

this.removeGalleryListSemantic();
this.updateImageSizes();
this.preventVariantURLSwitching();
super.show(opener);
})
.finally(() => {
this.bindProductChangeCallbacks();
opener.removeAttribute('aria-disabled');
opener.classList.remove('loading');
opener.querySelector('.loading__spinner').classList.add('hidden');
});
}

setInnerHTML(element, html) {
element.innerHTML = html;

// Reinjects the script tags to allow execution. By default, scripts are disabled when using element.innerHTML.
element.querySelectorAll('script').forEach((oldScriptTag) => {
const newScriptTag = document.createElement('script');
Array.from(oldScriptTag.attributes).forEach((attribute) => {
newScriptTag.setAttribute(attribute.name, attribute.value);
bindProductChangeCallbacks() {
const swapProductUtility = this.querySelector('variant-selects')?.swapProductUtility;
if (swapProductUtility) {
swapProductUtility.addPreProcessCallback(this.preprocessHTML.bind(this));
swapProductUtility.addPostProcessCallback(() => {
this.modalContent = this.querySelector('[id^="QuickAddInfo-"]');
this.bindProductChangeCallbacks();
});
newScriptTag.appendChild(document.createTextNode(oldScriptTag.innerHTML));
oldScriptTag.parentNode.replaceChild(newScriptTag, oldScriptTag);
}
}

preprocessHTML(productElement) {
productElement.classList.forEach((classApplied) => {
if (classApplied.startsWith('color-') || classApplied === 'gradient')
this.modalContent.classList.add(classApplied);
});
this.preventDuplicatedIDs(productElement);
this.removeDOMElements(productElement);
this.removeGalleryListSemantic(productElement);
this.updateImageSizes(productElement);
this.preventVariantURLSwitching(productElement);
}

preventVariantURLSwitching() {
const variantPicker = this.modalContent.querySelector('variant-selects');
preventVariantURLSwitching(productElement) {
const variantPicker = productElement.querySelector('variant-selects');
if (!variantPicker) return;

variantPicker.setAttribute('data-update-url', 'false');
}

removeDOMElements() {
const pickupAvailability = this.productElement.querySelector('pickup-availability');
removeDOMElements(productElement) {
const pickupAvailability = productElement.querySelector('pickup-availability');
if (pickupAvailability) pickupAvailability.remove();

const productModal = this.productElement.querySelector('product-modal');
const productModal = productElement.querySelector('product-modal');
if (productModal) productModal.remove();

const modalDialog = this.productElement.querySelectorAll('modal-dialog');
const modalDialog = productElement.querySelectorAll('modal-dialog');
if (modalDialog) modalDialog.forEach((modal) => modal.remove());
}

preventDuplicatedIDs() {
const sectionId = this.productElement.dataset.section;
this.productElement.innerHTML = this.productElement.innerHTML.replaceAll(sectionId, `quickadd-${sectionId}`);
this.productElement.querySelectorAll('variant-selects, product-info').forEach((element) => {
preventDuplicatedIDs(productElement) {
const sectionId = productElement.dataset.section;
productElement.innerHTML = productElement.innerHTML.replaceAll(sectionId, `quickadd-${sectionId}`);
productElement.querySelectorAll('variant-selects, product-info').forEach((element) => {
element.dataset.originalSection = sectionId;
});
}

removeGalleryListSemantic() {
const galleryList = this.modalContent.querySelector('[id^="Slider-Gallery"]');
removeGalleryListSemantic(productElement) {
const galleryList = productElement.querySelector('[id^="Slider-Gallery"]');
if (!galleryList) return;

galleryList.setAttribute('role', 'presentation');
galleryList.querySelectorAll('[id^="Slide-"]').forEach((li) => li.setAttribute('role', 'presentation'));
}

updateImageSizes() {
const product = this.modalContent.querySelector('.product');
updateImageSizes(productElement) {
const product = productElement.querySelector('.product');
const desktopColumns = product.classList.contains('product--columns');
if (!desktopColumns) return;

Expand Down
19 changes: 14 additions & 5 deletions assets/quick-order-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ class QuickOrderListRemoveAllButton extends HTMLElement {

customElements.define('quick-order-list-remove-all-button', QuickOrderListRemoveAllButton);


class QuickOrderList extends HTMLElement {
constructor() {
super();
Expand All @@ -80,6 +79,7 @@ class QuickOrderList extends HTMLElement {
}

cartUpdateUnsubscriber = undefined;
sectionRefreshUnsubscriber = undefined;

onSubmit(event) {
event.preventDefault();
Expand All @@ -91,15 +91,24 @@ class QuickOrderList extends HTMLElement {
return;
}
// If its another section that made the update
this.onCartUpdate();
this.refresh();
});

this.sectionRefreshUnsubscriber = subscribe(PUB_SUB_EVENTS.sectionRefreshed, (event) => {
const isParentSectionUpdated =
this.sectionId && (event.data?.sectionId ?? '') === `${this.sectionId.split('__')[0]}__main`;

if (isParentSectionUpdated) {
this.refresh();
}
});

this.sectionId = this.dataset.id;
}

disconnectedCallback() {
if (this.cartUpdateUnsubscriber) {
this.cartUpdateUnsubscriber();
}
this.cartUpdateUnsubscriber?.();
this.unsubscribeFromSectionRefresh?.();
}

onChange(event) {
Expand Down
7 changes: 6 additions & 1 deletion sections/main-product.liquid
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,12 @@
</p>
{%- endif -%}
{%- when 'complementary' -%}
<product-recommendations class="complementary-products quick-add-hidden no-js-hidden{% if block.settings.make_collapsible_row %} is-accordion{% endif %}{% if block.settings.enable_quick_add %} complementary-products-contains-quick-add{% endif %}" data-url="{{ routes.product_recommendations_url }}?section_id={{ section.id }}&product_id={{ product.id }}&limit={{ block.settings.product_list_limit }}&intent=complementary">
<product-recommendations
class="complementary-products quick-add-hidden no-js-hidden{% if block.settings.make_collapsible_row %} is-accordion{% endif %}{% if block.settings.enable_quick_add %} complementary-products-contains-quick-add{% endif %}"
data-url="{{ routes.product_recommendations_url }}?limit={{ block.settings.product_list_limit }}&intent=complementary"
data-section-id="{{ section.id }}"
data-product-id="{{ product.id }}"
>
{%- if recommendations.performed and recommendations.products_count > 0 -%}
<aside aria-label="{{ 'accessibility.complementary_products' | t }}" {{ block.shopify_attributes }}{% if block.settings.make_collapsible_row %} class="product__accordion accordion"{% endif %}>
<div class="complementary-products__container">
Expand Down
4 changes: 3 additions & 1 deletion sections/related-products.liquid
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@
<div class="color-{{ section.settings.color_scheme }} gradient no-js-hidden">
<product-recommendations
class="related-products page-width section-{{ section.id }}-padding isolate{% if settings.animations_reveal_on_scroll %} scroll-trigger animate--slide-in{% endif %}"
data-url="{{ routes.product_recommendations_url }}?section_id={{ section.id }}&product_id={{ product.id }}&limit={{ section.settings.products_to_show }}"
data-url="{{ routes.product_recommendations_url }}?limit={{ section.settings.products_to_show }}"
data-section-id="{{ section.id }}"
data-product-id="{{ product.id }}"
>
{% if recommendations.performed and recommendations.products_count > 0 %}
<h2 class="related-products__heading inline-richtext {{ section.settings.heading_size }}">
Expand Down
10 changes: 8 additions & 2 deletions snippets/buy-buttons.liquid
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,10 @@
type="hidden"
name="id"
value="{{ product.selected_or_first_available_variant.id }}"
{% if product.selected_or_first_available_variant.available == false or quantity_rule_soldout %}
{% if product.selected_or_first_available_variant.available == false
or quantity_rule_soldout
or product.selected_or_first_available_variant == nil
%}
disabled
{% endif %}
class="product-variant-id"
Expand All @@ -80,7 +83,10 @@
type="submit"
name="add"
class="product-form__submit button button--full-width {% if show_dynamic_checkout %}button--secondary{% else %}button--primary{% endif %}"
{% if product.selected_or_first_available_variant.available == false or quantity_rule_soldout %}
{% if product.selected_or_first_available_variant.available == false
or quantity_rule_soldout
or product.selected_or_first_available_variant == nil
%}
disabled
{% endif %}
>
Expand Down
2 changes: 2 additions & 0 deletions snippets/price.liquid
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
endif
-%}

{%- unless target == nil -%}
<div
class="
price
Expand Down Expand Up @@ -126,3 +127,4 @@
</span>
{%- endif -%}
</div>
{% endunless %}
55 changes: 23 additions & 32 deletions snippets/product-variant-options.liquid
Original file line number Diff line number Diff line change
Expand Up @@ -17,35 +17,11 @@
%}
{% endcomment %}
{%- liquid
assign variants_available_arr = product.variants | map: 'available'
assign variants_option1_arr = product.variants | map: 'option1'
assign variants_option2_arr = product.variants | map: 'option2'
assign variants_option3_arr = product.variants | map: 'option3'

assign product_form_id = 'product-form-' | append: section.id
-%}

{%- for value in option.values -%}
{%- liquid
assign option_disabled = true

for option1_name in variants_option1_arr
case option.position
when 1
if variants_option1_arr[forloop.index0] == value and variants_available_arr[forloop.index0]
assign option_disabled = false
endif
when 2
if option1_name == product.selected_or_first_available_variant.option1 and variants_option2_arr[forloop.index0] == value and variants_available_arr[forloop.index0]
assign option_disabled = false
endif
when 3
if option1_name == product.selected_or_first_available_variant.option1 and variants_option2_arr[forloop.index0] == product.selected_or_first_available_variant.option2 and variants_option3_arr[forloop.index0] == value and variants_available_arr[forloop.index0]
assign option_disabled = false
endif
endcase
endfor

if value.swatch.image
assign image_url = value.swatch.image | image_url: width: 50
assign swatch_value = 'url(' | append: image_url | append: ')'
Expand All @@ -54,33 +30,41 @@
else
assign swatch_value = nil
endif

assign option_disabled = true
if value.variant.available
assign option_disabled = false
endif
-%}

{%- capture input_id -%}
{{ section.id }}-{{ option.position }}-{{ forloop.index0 -}}
{%- endcapture -%}

{%- capture input_dataset -%}
data-available="{{ value.variant.available }}"
data-product-url="{{ value.product_url }}"
data-option-value-id="{{ value.id }}"
{%- endcapture -%}

{%- capture label_unavailable %}
<span class="visually-hidden label-unavailable">
{{- 'products.product.variant_sold_out_or_unavailable' | t -}}
</span>
{%- endcapture %}

{%- if picker_type == 'swatch' -%}
{% assign checked = false %}
{% if option.selected_value == value %}
{% assign checked = true %}
{% endif %}
{%
render 'swatch-input',
id: input_id,
name: option.name,
value: value | escape,
product_form_id: product_form_id,
checked: checked,
checked: value.selected,
disabled: option_disabled,
shape: block.settings.swatch_shape,
help_text: label_unavailable
help_text: label_unavailable,
additional_props: input_dataset
%}
{%- elsif picker_type == 'button' -%}
<input
Expand All @@ -89,26 +73,29 @@
name="{{ option.name }}"
value="{{ value | escape }}"
form="{{ product_form_id }}"
{% if option.selected_value == value %}
{% if value.selected %}
checked
{% endif %}
{% if option_disabled %}
class="disabled"
{% endif %}
{{ input_dataset }}
>
<label for="{{ input_id }}">
{{ value -}}
{{ label_unavailable }}
</label>
{%- elsif picker_type == 'dropdown' or picker_type == 'swatch_dropdown' -%}
<option
id="{{ input_id }}"
value="{{ value | escape }}"
{% if option.selected_value == value %}
{% if value.selected %}
selected="selected"
{% endif %}
{% if swatch_value and picker_type == 'swatch_dropdown' %}
data-option-swatch-value="{{ swatch_value }}"
{% endif %}
{{ input_dataset }}
>
{% if option_disabled -%}
{{- 'products.product.value_unavailable' | t: option_value: value -}}
Expand All @@ -117,4 +104,8 @@
{%- endif %}
</option>
{%- endif -%}

<script type="application/json" data-resource="{{ input_id }}">
{{ value.variant | json }}
</script>
{%- endfor -%}
Loading

0 comments on commit 8f6fe02

Please sign in to comment.