diff --git a/promotions/app/javascript/backend/solidus_promotions/controllers/product_option_values_controller.js b/promotions/app/javascript/backend/solidus_promotions/controllers/product_option_values_controller.js index 3b8f0c9fbe8..ebf0d1b442b 100644 --- a/promotions/app/javascript/backend/solidus_promotions/controllers/product_option_values_controller.js +++ b/promotions/app/javascript/backend/solidus_promotions/controllers/product_option_values_controller.js @@ -6,8 +6,6 @@ export default class extends Controller { connect() { this.wrapperClass = this.data.get("wrapperClass") || "promo-condition-option-value"; - - this.element.querySelectorAll("." + this.wrapperClass).forEach((element) => this.buildSelects(element)) } add_row(event) { @@ -15,7 +13,6 @@ export default class extends Controller { var content = this.templateTarget.innerHTML; this.linksTarget.insertAdjacentHTML("beforebegin", content); - this.buildSelects(this.linksTarget.previousElementSibling) } propagate_product_id_to_value_input(event) { @@ -24,7 +21,9 @@ export default class extends Controller { // we first need to greedily match all other square brackets const regEx = /(\[.*\])\[.*?\]$/; let wrapper = event.target.closest("." + this.wrapperClass); - let optionValuesInput = wrapper.querySelector(".option-values-select[type='hidden']"); + let optionValuesInput = wrapper.querySelector("[is=option-value-picker]"); + optionValuesInput.dataset.productId = event.target.value; + optionValuesInput.value = ""; optionValuesInput.name = optionValuesInput.name.replace( regEx, `$1[${event.target.value}]` @@ -37,26 +36,4 @@ export default class extends Controller { let wrapper = event.target.closest("." + this.wrapperClass); wrapper.remove(); } - - // helper functions - - buildSelects(wrapper) { - let productSelect = wrapper.querySelector(".product-select") - let optionValueSelect = wrapper.querySelector(".option-values-select[type='hidden']") - this.buildProductSelect(productSelect) - $(optionValueSelect).optionValueAutocomplete({ productSelect }); - } - - buildProductSelect(productSelect) { - var jQueryProductSelect = $(productSelect) - jQueryProductSelect.productAutocomplete({ - multiple: false, - }) - // capture the jQuery "change" event and re-emit it as DOM event "select2Change" - // so that Stimulus can capture it - jQueryProductSelect.on('change', function () { - let event = new Event('select2Change', { bubbles: true }) // fire a native event - productSelect.dispatchEvent(event); - }); - } } diff --git a/promotions/app/javascript/backend/solidus_promotions/web_components/option_value_picker.js b/promotions/app/javascript/backend/solidus_promotions/web_components/option_value_picker.js index a5f7d0489a9..6e384a994ae 100644 --- a/promotions/app/javascript/backend/solidus_promotions/web_components/option_value_picker.js +++ b/promotions/app/javascript/backend/solidus_promotions/web_components/option_value_picker.js @@ -1,11 +1,9 @@ $.fn.optionValueAutocomplete = function (options) { - 'use strict'; + "use strict"; // Default options - options = options || {} - var multiple = typeof(options['multiple']) !== 'undefined' ? options['multiple'] : true; - var productSelect = options['productSelect']; - + options = options || {}; + var multiple = typeof options["multiple"] !== "undefined" ? options["multiple"] : true; function formatOptionValue(option_value) { return Select2.util.escapeMarkup(option_value.name); } @@ -14,39 +12,58 @@ $.fn.optionValueAutocomplete = function (options) { minimumInputLength: 3, multiple: multiple, initSelection: function (element, callback) { - $.get(Spree.pathFor('api/option_values'), { - ids: element.val().split(','), - token: Spree.api_key - }, function (data) { - callback(multiple ? data : data[0]); - }); + $.get( + Spree.pathFor("api/option_values"), + { + ids: element.val().split(","), + token: Spree.api_key, + }, + function (data) { + callback(multiple ? data : data[0]); + } + ); }, ajax: { - url: Spree.pathFor('api/option_values'), - datatype: 'json', + url: Spree.pathFor("api/option_values"), + datatype: "json", data: function (term, page) { - var productId = typeof(productSelect) !== 'undefined' ? $(productSelect).select2('val') : null; + var productId = this[0].dataset.productId; return { q: { name_cont: term, - variants_product_id_eq: productId + variants_product_id_eq: productId, }, - token: Spree.api_key + token: Spree.api_key, }; }, results: function (data, page) { return { results: data }; - } + }, }, formatResult: formatOptionValue, - formatSelection: formatOptionValue + formatSelection: formatOptionValue, }); }; class OptionValuePicker extends HTMLInputElement { connectedCallback() { $(this).optionValueAutocomplete(); + + this.observer = new MutationObserver((muts) => { + for (const m of muts) { + if (m.attributeName.startsWith("data-product-id")) { + this.restart(); + } + } + }); + + this.observer.observe(this, { attributes: true }); + } + + restart() { + $(this).select2("destroy"); + $(this).optionValueAutocomplete(); } } -customElements.define('option-value-picker', OptionValuePicker, { extends: 'input' }); +customElements.define("option-value-picker", OptionValuePicker, { extends: "input" }); diff --git a/promotions/app/javascript/backend/solidus_promotions/web_components/product_picker.js b/promotions/app/javascript/backend/solidus_promotions/web_components/product_picker.js index 02cc58e6840..576fd60f68e 100644 --- a/promotions/app/javascript/backend/solidus_promotions/web_components/product_picker.js +++ b/promotions/app/javascript/backend/solidus_promotions/web_components/product_picker.js @@ -1,6 +1,11 @@ class ProductPicker extends HTMLInputElement { connectedCallback() { - $(this).productAutocomplete(); + const multiple = this.dataset.multiple !== "false"; + $(this).productAutocomplete({ multiple }); + $(this).on("change", (_) => { + let event = new Event('select2Change', { bubbles: true }) // fire a native event + this.dispatchEvent(event) + }) } } diff --git a/promotions/lib/views/backend/solidus_promotions/admin/condition_fields/_line_item_option_value.html.erb b/promotions/lib/views/backend/solidus_promotions/admin/condition_fields/_line_item_option_value.html.erb index 59d8818c1b4..17a51dfab4c 100644 --- a/promotions/lib/views/backend/solidus_promotions/admin/condition_fields/_line_item_option_value.html.erb +++ b/promotions/lib/views/backend/solidus_promotions/admin/condition_fields/_line_item_option_value.html.erb @@ -5,19 +5,20 @@
-
<%= label_tag nil, Spree::Product.model_name.human %>
+
<%= label_tag nil, Spree::Product.model_name.human %>
<%= label_tag nil, plural_resource_name(Spree::OptionValue) %>
+
 
- <% form.object.preferred_eligible_values.each do |product_option_values| %> - <%= render "solidus_promotions/admin/condition_fields/line_item_option_value/option_value_fields", product_option_values: product_option_values, form: form %> + <% form.object.preferred_eligible_values.each.with_index do |product_option_values, index| %> + <%= render "solidus_promotions/admin/condition_fields/line_item_option_value/option_value_fields", product_option_values:, form:, index: %> <% end %> -
+
<%= link_to t(:add_product, scope: [:solidus_promotions, :line_item_option_value_condition]), "#", class: "btn btn-outline-primary", data: { action: "click->product-option-values#add_row" } %>
diff --git a/promotions/lib/views/backend/solidus_promotions/admin/condition_fields/_option_value.html.erb b/promotions/lib/views/backend/solidus_promotions/admin/condition_fields/_option_value.html.erb index 7f6581bef82..24b388c8a96 100644 --- a/promotions/lib/views/backend/solidus_promotions/admin/condition_fields/_option_value.html.erb +++ b/promotions/lib/views/backend/solidus_promotions/admin/condition_fields/_option_value.html.erb @@ -7,19 +7,20 @@
-
<%= label_tag nil, Spree::Product.model_name.human %>
+
<%= label_tag nil, Spree::Product.model_name.human %>
<%= label_tag nil, plural_resource_name(Spree::OptionValue) %>
+
 
- <% form.object.preferred_eligible_values.each do |product_option_values| %> - <%= render "solidus_promotions/admin/condition_fields/line_item_option_value/option_value_fields", product_option_values: product_option_values, form: form %> + <% form.object.preferred_eligible_values.each.with_index do |product_option_values, index| %> + <%= render "solidus_promotions/admin/condition_fields/line_item_option_value/option_value_fields", product_option_values:, form:, index: %> <% end %> -
+
<%= link_to t(:add_product, scope: [:solidus_promotions, :option_value_condition]), "#", class: "btn btn-outline-primary", data: { action: "click->product-option-values#add_row" } %>
diff --git a/promotions/lib/views/backend/solidus_promotions/admin/condition_fields/line_item_option_value/_option_value_fields.html.erb b/promotions/lib/views/backend/solidus_promotions/admin/condition_fields/line_item_option_value/_option_value_fields.html.erb index b8081a854fa..add363ef189 100644 --- a/promotions/lib/views/backend/solidus_promotions/admin/condition_fields/line_item_option_value/_option_value_fields.html.erb +++ b/promotions/lib/views/backend/solidus_promotions/admin/condition_fields/line_item_option_value/_option_value_fields.html.erb @@ -1,21 +1,27 @@ -
-
+
+
-
+
" >
- +
+ +
diff --git a/promotions/spec/system/solidus_promotions/backend/promotions_spec.rb b/promotions/spec/system/solidus_promotions/backend/promotions_spec.rb index 65d481c8873..acc9fc3bf2c 100644 --- a/promotions/spec/system/solidus_promotions/backend/promotions_spec.rb +++ b/promotions/spec/system/solidus_promotions/backend/promotions_spec.rb @@ -162,6 +162,30 @@ end end + describe "Adding a benefit condition" do + let!(:promotion_with_benefit) { create(:solidus_promotion, :with_adjustable_benefit) } + let!(:product) { create(:product) } + let!(:option_value) { create(:option_value) } + let!(:variant) { create(:variant, product: product, option_values: [option_value]) } + + it "allows adding an option value condition", :js do + visit solidus_promotions.edit_admin_promotion_path(promotion_with_benefit) + click_link "Add Condition" + select("Line Item Option Value(s)", from: "Condition Type") + click_button "Add" + expect(page).to have_content("Line Item Option Value(s)") + click_link "Add product" + within(".promo-condition-option-value") do + targetted_select2_search(product.name, from: "#s2id_ov-product-picker-0") + targetted_select2_search(option_value.name, from: "#s2id_option-value-picker-0") + end + within("#benefits_adjust_line_item_#{promotion_with_benefit.benefits.first.id}_conditions") do + click_button("Update") + end + expect(page).to have_content("Condition has been successfully updated!") + end + end + describe "Rendering the promotion edit page with the PercentWithCap calculator" do let(:promotion) { create(:solidus_promotion, name: "My capped promotion", benefits: [benefit]) } let(:benefit) { SolidusPromotions::Benefits::AdjustLineItem.new(calculator:) }