diff --git a/app/components/avo/fields/select_field/edit_component.html.erb b/app/components/avo/fields/select_field/edit_component.html.erb index 3234e1c8fc..6b92530928 100644 --- a/app/components/avo/fields/select_field/edit_component.html.erb +++ b/app/components/avo/fields/select_field/edit_component.html.erb @@ -2,6 +2,7 @@ <%= @form.select @field.id, options_for_select(@field.options_for_select, selected: @field.value), { include_blank: @field.include_blank }, + multiple: @field.multiple, aria: { placeholder: @field.placeholder }, diff --git a/app/components/avo/fields/select_field/show_component.html.erb b/app/components/avo/fields/select_field/show_component.html.erb index 1e187c3f0a..70916d4132 100644 --- a/app/components/avo/fields/select_field/show_component.html.erb +++ b/app/components/avo/fields/select_field/show_component.html.erb @@ -1,3 +1,3 @@ <%= field_wrapper(**field_wrapper_args.merge(dash_if_blank: false)) do %> - <%= @field.value.nil? ? "—" : @field.label %> + <%= @field.label %> <% end %> diff --git a/db/migrate/20250103192051_add_sizes_to_products.rb b/db/migrate/20250103192051_add_sizes_to_products.rb new file mode 100644 index 0000000000..012f84f74c --- /dev/null +++ b/db/migrate/20250103192051_add_sizes_to_products.rb @@ -0,0 +1,5 @@ +class AddSizesToProducts < ActiveRecord::Migration[6.1] + def change + add_column :products, :sizes, :string, array: true, default: [] + end +end diff --git a/lib/avo/fields/select_field.rb b/lib/avo/fields/select_field.rb index d28f7dba20..9cf21251cc 100644 --- a/lib/avo/fields/select_field.rb +++ b/lib/avo/fields/select_field.rb @@ -3,7 +3,7 @@ module Fields class SelectField < BaseField include Avo::Fields::FieldExtensions::HasIncludeBlank - attr_reader :display_value + attr_reader :display_value, :multiple def initialize(id, **args, &block) args[:placeholder] ||= I18n.t("avo.choose_an_option") @@ -19,6 +19,7 @@ def initialize(id, **args, &block) end @enum = args[:enum] + @multiple = args[:multiple] @display_value = args[:display_value] || false end @@ -40,8 +41,12 @@ def options_for_select end def label + return "—" if value.nil? || (@multiple && value.empty?) + # If options are array don't need any pre-process - return value if options.is_a?(Array) + if options.is_a?(Array) + return @multiple ? value.join(", ") : value + end # If options are enum and display_value is true we return the Value of that key-value pair, else return key of that key-value pair # WARNING: value here is the DB stored value and not the value of a key-value pair. @@ -52,7 +57,25 @@ def label # When code arrive here it means options are Hash # If display_value is true we only need to return the value stored in DB - display_value ? value : options.invert[value] + if display_value + value + elsif @multiple + options.select { |_, v| value.include?(v.to_s) }.keys.join(", ") + else + options.invert[value] + end + end + + def to_permitted_param + @multiple ? {"#{id}": []} : id + end + + def fill_field(record, key, value, params) + if @multiple + value = value.reject(&:blank?) + end + + super end private diff --git a/spec/dummy/app/avo/resources/product.rb b/spec/dummy/app/avo/resources/product.rb index 442bf323a0..467cd83c87 100644 --- a/spec/dummy/app/avo/resources/product.rb +++ b/spec/dummy/app/avo/resources/product.rb @@ -48,5 +48,6 @@ def fields field :description, as: :tiptap, placeholder: "Enter text", always_show: false field :image, as: :file, is_image: true field :category, as: :select, enum: ::Product.categories + field :sizes, as: :select, multiple: true, options: {Large: :large, Medium: :medium, Small: :small} end end diff --git a/spec/dummy/db/schema.rb b/spec/dummy/db/schema.rb index 04326b29e1..4a41fc805e 100644 --- a/spec/dummy/db/schema.rb +++ b/spec/dummy/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[8.0].define(version: 2024_11_14_165947) do +ActiveRecord::Schema[8.0].define(version: 2025_01_03_192051) do # These are extensions that must be enabled in order to support this database enable_extension "pg_catalog.plpgsql" @@ -173,6 +173,7 @@ t.datetime "updated_at", null: false t.integer "price_cents", default: 0, null: false t.string "price_currency", default: "'USD'::character varying", null: false + t.string "sizes", default: [], array: true end create_table "projects", force: :cascade do |t| diff --git a/spec/features/avo/select_field_spec.rb b/spec/features/avo/select_field_spec.rb index 62bac79102..ff85854ac7 100644 --- a/spec/features/avo/select_field_spec.rb +++ b/spec/features/avo/select_field_spec.rb @@ -229,4 +229,53 @@ def test_array end end end + + describe "when options are multiple select" do + context "new" do + it "allow selection of multiple values" do + visit avo.new_resources_product_path + + expect(page).to have_select "product_sizes", multiple: true, options: ["Large", "Medium", "Small"] + + select "Large", from: "product_sizes" + select "Medium", from: "product_sizes" + + save + + expect(find_field_element(:sizes)).to have_text("Large, Medium") + expect(Product.last.sizes).to match_array(["large", "medium"]) + end + end + + context "edit" do + let(:product) { create :product, sizes: [:large] } + + it "allow changing of selected values" do + visit avo.edit_resources_product_path(product) + + expect(page).to have_select "product_sizes", selected: ["Large"], multiple: true, options: ["Large", "Medium", "Small"] + + select "Medium", from: "product_sizes" + select "Small", from: "product_sizes" + + save + + expect(find_field_element(:sizes)).to have_text("Large, Medium, Small") + expect(Product.last.sizes).to match_array(["medium", "small", "large"]) + end + + it "allow deselecting of previously selected values" do + visit avo.edit_resources_product_path(product) + + expect(page).to have_select "product_sizes", selected: ["Large"], multiple: true, options: ["Large", "Medium", "Small"] + + page.unselect "Large", from: "Sizes" + + save + + expect(find_field_element(:sizes)).to have_text("—") + expect(Product.last.sizes).to match_array([]) + end + end + end end