Iâve found a solution that Iâve implemented into a v15 dev branch at my work. I did the following:
The source of truth
At the top of the main-product liquid file, Iâm using a variable âselected_variantâ as a main source of truth for the currently selected variant, and re-using it across various snippets that refer to variant info (price, inventory, etc.).
By default, âselected_variantâ will remain null, unless content_for_header contains the option_values parameter in the url.
{%- capture content_for_querystring -%}{{ content_for_header }}{%- endcapture -%}
{% liquid
assign query_parts = content_for_querystring | split: '"pageurl":"' | last | split: '"' | first | split: '://' | last | split: '/'
if query_parts.size > 1
assign domain_removed_parts = query_parts | slice: 1, query_parts.size
assign final_url = domain_removed_parts | join: '/' | prepend: '/'
else
assign final_url = '/'
endif
assign final_url = final_url | replace: '\/', '/' | replace: '%20', ' ' | replace: '\u0026', '&'
assign is_variant_selected = false
if final_url contains 'option_values=' or product.selected_variant.id or product.has_only_default_variant or section.settings.auto_set_variant == true
assign is_variant_selected = true
endif
assign selected_variant = null
if is_variant_selected
assign selected_variant = product.selected_or_first_available_variant
endif
%}
Option Selection Part 1
Inside the âproduct-variant-optionsâ snippet, we need to only show selected state on option values if âselected_variantâ isnât blank. So all we need to do here is add another check to the lines rendering the checked state on the inputs and selects.
Example:
{% if value.selected and selected_variant %}
selected="selected"
{% endif %}
Option Selection Part 2
On products where multiple groups of options are present, this will misbehave. Unless we automatically select remaining option values, it will select the first available variant regardless of your selection.
Example: A product has color and size options, clicking âLGâ on size will select the first color and the smallest size instead of LG.
To fix this, Iâve created a getter function on the âVariantSelectsâ class to return currently selected and missing selected values for options, and passed that value to the change event.
connectedCallback() {
this.addEventListener('change', (event) => {
const target = this.getInputForEventTarget(event.target);
this.updateSelectionMetadata(event);
publish(PUB_SUB_EVENTS.optionValueSelectionChange, {
data: {
event,
target,
selectedOptionValues: this.completeOptionValuesSet,
// ^^^ use getter here
},
});
});
}
// ...
// new getter
get completeOptionValuesSet() {
const selectionOptionValues = this.selectedOptionValues;
const optionGroups = Array.from(this.querySelectorAll('fieldset, select'));
if (selectionOptionValues.length === optionGroups.length) return selectionOptionValues;
let completeOptionValuesSet = [];
optionGroups.forEach((group) => {
const selectedOption = group.querySelector('input:checked', 'select option[selected]');
if (selectedOption) {
completeOptionValuesSet.push(selectedOption.dataset.optionValueId);
} else {
const firstAvailableOption = group.querySelector('input:not(:disabled), option:not(:disabled)');
firstAvailableOption && completeOptionValuesSet.push(firstAvailableOption.dataset.optionValueId);
}
})
return completeOptionValuesSet;
}
I may or may not have missed some edge cases, but this is the backbone of our implementation. I also havenât tested the dropdown variant picker mode.