diff --git a/app/components/searchable_dropdown_component.rb b/app/components/searchable_dropdown_component.rb index 7a6992504e..98365c4c8b 100644 --- a/app/components/searchable_dropdown_component.rb +++ b/app/components/searchable_dropdown_component.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true class SearchableDropdownComponent < ViewComponent::Base - REMOVED_SEARCH_PLUGIN = {"tom-select-options-value": '{ "plugins": [] }'}.freeze - MINIMUM_OPTIONS_FOR_SEARCH_FILED = 11.freeze # at least 11 options are required for the search field + REMOVED_SEARCH_PLUGIN = { 'tom-select-options-value': '{ "plugins": [] }' }.freeze + MINIMUM_OPTIONS_FOR_SEARCH_FILED = 11 # at least 11 options are required for the search field def initialize( form:, @@ -10,7 +10,8 @@ class SearchableDropdownComponent < ViewComponent::Base options:, selected_option:, placeholder_value:, - include_blank: false + include_blank: false, + aria_label: '' ) @f = form @name = name @@ -18,11 +19,12 @@ class SearchableDropdownComponent < ViewComponent::Base @selected_option = selected_option @placeholder_value = placeholder_value @include_blank = include_blank + @aria_label = aria_label end private - attr_reader :f, :name, :options, :selected_option, :placeholder_value, :include_blank + attr_reader :f, :name, :options, :selected_option, :placeholder_value, :include_blank, :aria_label def classes "fullwidth #{remove_search_plugin? ? 'no-input' : ''}" @@ -30,7 +32,7 @@ class SearchableDropdownComponent < ViewComponent::Base def data { - "controller": "tom-select", + controller: "tom-select", 'tom-select-placeholder-value': placeholder_value }.merge(remove_search_plugin? ? REMOVED_SEARCH_PLUGIN : {}) end diff --git a/app/components/searchable_dropdown_component/searchable_dropdown_component.html.haml b/app/components/searchable_dropdown_component/searchable_dropdown_component.html.haml index fbff09f1bf..10043eaa29 100644 --- a/app/components/searchable_dropdown_component/searchable_dropdown_component.html.haml +++ b/app/components/searchable_dropdown_component/searchable_dropdown_component.html.haml @@ -1 +1 @@ -= f.select name, options_for_select(options, selected_option), { include_blank: }, class: classes, data: += f.select name, options_for_select(options, selected_option), { include_blank: }, class: classes, data:, 'aria-label': aria_label diff --git a/app/views/admin/products_v3/_product_row.html.haml b/app/views/admin/products_v3/_product_row.html.haml index 06483a93df..3c12ac1b1b 100644 --- a/app/views/admin/products_v3/_product_row.html.haml +++ b/app/views/admin/products_v3/_product_row.html.haml @@ -30,6 +30,7 @@ %td.naked_inputs.align-left = render(SearchableDropdownComponent.new(form: f, name: :supplier_id, + aria_label: t('.producer_field_name'), options: producer_options, selected_option: product.supplier_id, placeholder_value: t('admin.products_v3.filters.search_for_producers'))) diff --git a/app/views/admin/products_v3/_variant_row.html.haml b/app/views/admin/products_v3/_variant_row.html.haml index 936ede26a6..53ea27aad5 100644 --- a/app/views/admin/products_v3/_variant_row.html.haml +++ b/app/views/admin/products_v3/_variant_row.html.haml @@ -46,6 +46,7 @@ name: :primary_taxon_id, options: category_options, selected_option: variant.primary_taxon_id, + aria_label: t('.category_field_name'), placeholder_value: t('admin.products_v3.filters.search_for_categories'))) %td.field.naked_inputs = render(SearchableDropdownComponent.new(form: f, @@ -53,6 +54,7 @@ options: tax_category_options, selected_option: variant.tax_category_id, include_blank: t('.none_tax_category'), + aria_label: t('.tax_category_field_name'), placeholder_value: t('.search_for_tax_categories'))) %td.align-left -# empty diff --git a/config/locales/en.yml b/config/locales/en.yml index f17baa2601..46197090c0 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -911,6 +911,10 @@ en: variant_row: none_tax_category: None search_for_tax_categories: "Search for tax categories" + category_field_name: "Category" + tax_category_field_name: "Tax Category" + product_row: + producer_field_name: "Producer" product_import: title: Product Import file_not_found: File not found or could not be opened diff --git a/spec/system/admin/products_v3/products_spec.rb b/spec/system/admin/products_v3/products_spec.rb index aa9cee9512..a39c1ac753 100644 --- a/spec/system/admin/products_v3/products_spec.rb +++ b/spec/system/admin/products_v3/products_spec.rb @@ -14,6 +14,10 @@ describe 'As an enterprise user, I can manage my products', feature: :admin_styl login_as user end + let(:producer_search_selector) { 'input[placeholder="Search for producers"]' } + let(:categories_search_selector) { 'input[placeholder="Search for categories"]' } + let(:tax_categories_search_selector) { 'input[placeholder="Search for tax categories"]' } + it "can see the new product page" do visit admin_products_url expect(page).to have_content "Bulk Edit Products" @@ -666,6 +670,113 @@ describe 'As an enterprise user, I can manage my products', feature: :admin_styl end end + describe "Changing producers, category and tax category" do + let!(:variant_a1) { + product_a.variants.first.tap{ |v| + v.update! display_name: "Medium box", sku: "APL-01", price: 5.25, on_hand: 5, + on_demand: false + } + } + let!(:product_a) { + create(:simple_product, name: "Apples", sku: "APL-00", + variant_unit: "weight", variant_unit_scale: 1) # Grams + } + + context "when they are under 11" do + before do + create_list(:supplier_enterprise, 9) + create_list(:tax_category, 9) + create_list(:taxon, 2) + + visit admin_products_url + end + + it "should not display search input, change the producers, category and tax category" do + producer_to_select = random_producer(product_a) + category_to_select = random_category(variant_a1) + tax_category_to_select = random_tax_category + + within row_containing_name(product_a.name) do + validate_tomselect_without_search!( + page, "Producer", + producer_to_select, + producer_search_selector + ) + end + + within row_containing_name(variant_a1.display_name) do + validate_tomselect_without_search!( + page, "Category", + category_to_select, + categories_search_selector + ) + validate_tomselect_without_search!( + page, "Tax Category", + tax_category_to_select, + tax_categories_search_selector + ) + end + + click_button "Save changes" + + expect(page).to have_content "Changes saved" + product_a.reload + variant_a1.reload + + expect(product_a.supplier.name).to eq(producer_to_select) + expect(variant_a1.primary_taxon.name).to eq(category_to_select) + expect(variant_a1.tax_category.name).to eq(tax_category_to_select) + end + end + + context "when they are over 11" do + before do + create_list(:supplier_enterprise, 11) + create_list(:tax_category, 11) + create_list(:taxon, 11) + + visit admin_products_url + end + + it "should display search input, change the producer" do + producer_to_select = random_producer(product_a) + category_to_select = random_category(variant_a1) + tax_category_to_select = random_tax_category + + within row_containing_name(product_a.name) do + validate_tomselect_with_search!( + page, "Producer", + producer_to_select, + producer_search_selector + ) + end + + within row_containing_name(variant_a1.display_name) do + validate_tomselect_with_search!( + page, "Category", + category_to_select, + categories_search_selector + ) + validate_tomselect_with_search!( + page, "Tax Category", + tax_category_to_select, + tax_categories_search_selector + ) + end + + click_button "Save changes" + + expect(page).to have_content "Changes saved" + product_a.reload + variant_a1.reload + + expect(product_a.supplier.name).to eq(producer_to_select) + expect(variant_a1.primary_taxon.name).to eq(category_to_select) + expect(variant_a1.tax_category.name).to eq(tax_category_to_select) + end + end + end + describe "edit image" do shared_examples "updating image" do it "saves product image" do @@ -1032,4 +1143,41 @@ describe 'As an enterprise user, I can manage my products', feature: :admin_styl def tax_category_column @tax_category_column ||= '[data-controller="variant"] > td:nth-child(10)' end + + def validate_tomselect_without_search!(page, field_name, value, search_selector) + tomselect_wrapper = page.find_field(field_name).sibling(".ts-wrapper") + tomselect_wrapper.find(".ts-control").click + + expect(page).not_to have_selector(search_selector) + + tomselect_wrapper.find(:css, '.ts-dropdown .ts-dropdown-content .option', + text: value).click + end + + def validate_tomselect_with_search!(page, field_name, value, search_selector) + tomselect_wrapper = page.find_field(field_name).sibling(".ts-wrapper") + tomselect_wrapper.find(".ts-control").click + + expect(page).to have_selector(search_selector) + + tomselect_wrapper.find(:css, '.ts-dropdown input.dropdown-input').send_keys(value) + tomselect_wrapper.find(:css, '.ts-dropdown .ts-dropdown-content .option', text: value).click + end + + def random_producer(product) + Enterprise.is_primary_producer + .where.not(id: product.supplier.id) + .pluck(:name).sample + end + + def random_category(variant) + Spree::Taxon + .where.not(id: variant.primary_taxon.id) + .pluck(:name).sample + end + + def random_tax_category + Spree::TaxCategory + .pluck(:name).sample + end end