Skip to content

Commit

Permalink
feature: Implement multiple option for select field (#3572)
Browse files Browse the repository at this point in the history
* Implement multiple select feature for the sizes column of the products table

* Update select_field.rb

Co-authored-by: Paul Bob <[email protected]>

* Update select_field_spec.rb

Co-authored-by: Paul Bob <[email protected]>

* Update select_field_spec.rb

Co-authored-by: Paul Bob <[email protected]>

* Update to multiple select PR

* Update select field spec file

* Move sanitization of multiple select field to be handled by the controller instead of model

* Fix Lint issues raised by Rubocop

* Fix migration version error thrown with rails 8 plus lint issues

* fix migration version

* Update spec/dummy/db/schema.rb

* fix select show component when `multiple: true`

* show array on display_value

* fix multiple syntax

* show component tests

* lint

---------

Co-authored-by: Paul Bob <[email protected]>
Co-authored-by: Paul Bob <[email protected]>
  • Loading branch information
3 people authored Feb 6, 2025
1 parent f806429 commit 2f82224
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
<%= field_wrapper(**field_wrapper_args.merge(dash_if_blank: false)) do %>
<%= @field.value.nil? ? "—" : @field.label %>
<%= @field.label %>
<% end %>
5 changes: 5 additions & 0 deletions db/migrate/20250103192051_add_sizes_to_products.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddSizesToProducts < ActiveRecord::Migration[6.1]
def change
add_column :products, :sizes, :string, array: true, default: []
end
end
29 changes: 26 additions & 3 deletions lib/avo/fields/select_field.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -19,6 +19,7 @@ def initialize(id, **args, &block)
end

@enum = args[:enum]
@multiple = args[:multiple]
@display_value = args[:display_value] || false
end

Expand All @@ -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.
Expand All @@ -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
Expand Down
1 change: 1 addition & 0 deletions spec/dummy/app/avo/resources/product.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
3 changes: 2 additions & 1 deletion spec/dummy/db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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|
Expand Down
49 changes: 49 additions & 0 deletions spec/features/avo/select_field_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

0 comments on commit 2f82224

Please sign in to comment.