Skip to content

Commit

Permalink
Metaprogram customizable element methods
Browse files Browse the repository at this point in the history
  • Loading branch information
josefarias committed Mar 8, 2024
1 parent bcc97f3 commit 6a5c98d
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 67 deletions.
59 changes: 4 additions & 55 deletions app/presenters/hotwire_combobox/component.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
require "securerandom"

class HotwireCombobox::Component
include Customizable

attr_reader :options, :dialog_label

def initialize \
Expand All @@ -26,7 +28,6 @@ def initialize \

@combobox_attrs = input.reverse_merge(rest).deep_symbolize_keys
@association_name = association_name || infer_association_name
@custom_attrs = Hash.new { |h, k| h[k] = {} }
end

def render_in(view_context, &block)
Expand All @@ -35,23 +36,6 @@ def render_in(view_context, &block)
end


def customize(element, **attrs)
element = element.to_sym.presence_in(CUSTOMIZABLE_ELEMENTS) ||
raise(ArgumentError, <<~MSG)
[ACTION NEEDED] – Message from HotwireCombobox:
You tried to customize an element called `#{element}`, but
HotwireCombobox does not recognize that element.
Please choose one of the valid elements: #{CUSTOMIZABLE_ELEMENTS.join(", ")}.
MSG

@custom_attrs[element] = attrs.deep_symbolize_keys.delete_if do |key, _|
PROTECTED_ATTRS.include? key
end
end


def fieldset_attrs
apply_customizations_to :fieldset, base: {
class: "hw-combobox",
Expand Down Expand Up @@ -81,7 +65,7 @@ def input_attrs
data: input_data,
aria: input_aria,
autocomplete: :off
}.with_indifferent_access.merge combobox_attrs.except(*nested_attrs)
}.merge combobox_attrs.except(*nested_attrs)

apply_customizations_to :input, base: base
end
Expand Down Expand Up @@ -165,44 +149,9 @@ def pagination_attrs
end

private
CUSTOMIZABLE_ELEMENTS = %i[
fieldset
hidden_field
input
handle
listbox
dialog
dialog_wrapper
dialog_label
dialog_input
dialog_listbox
].freeze

PROTECTED_ATTRS = %i[
id
name
value
open
role
hidden
].freeze

attr_reader :view, :autocomplete, :id, :name, :value, :form,
:name_when_new, :open, :data, :combobox_attrs, :mobile_at,
:association_name, :custom_attrs

def apply_customizations_to(element, base: {})
custom = custom_attrs[element]
default = base.deep_symbolize_keys.map do |key, value|
if value.is_a?(String) || value.is_a?(Symbol)
[ key, view.token_list(value.to_s, custom.delete(key)) ]
else
[ key, value ]
end
end.to_h

custom.deep_merge default
end
:association_name

def infer_association_name
if name.include?("_id")
Expand Down
63 changes: 63 additions & 0 deletions app/presenters/hotwire_combobox/component/customizable.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
module HotwireCombobox::Component::Customizable
CUSTOMIZABLE_ELEMENTS = %i[
fieldset
hidden_field
input
handle
listbox
dialog
dialog_wrapper
dialog_label
dialog_input
dialog_listbox
].freeze

PROTECTED_ATTRS = %i[
id
name
value
open
role
hidden
].freeze

CUSTOMIZABLE_ELEMENTS.each do |element|
define_method "customize_#{element}" do |**attrs|
customize element, **attrs
end
end

private
def custom_attrs
@custom_attrs ||= Hash.new { |h, k| h[k] = {} }
end

def customize(element, **attrs)
element = element.to_sym.presence_in(CUSTOMIZABLE_ELEMENTS) ||
raise(ArgumentError, <<~MSG)
[ACTION NEEDED] – Message from HotwireCombobox:
You tried to customize an element called `#{element}`, but
HotwireCombobox does not recognize that element.
Please choose one of the valid elements: #{CUSTOMIZABLE_ELEMENTS.join(", ")}.
MSG

custom_attrs[element] = attrs.deep_symbolize_keys.delete_if do |key, _|
PROTECTED_ATTRS.include? key
end
end

def apply_customizations_to(element, base: {})
custom = custom_attrs[element]
default = base.deep_symbolize_keys.map do |key, value|
if value.is_a?(String) || value.is_a?(Symbol)
[ key, view.token_list(value.to_s, custom.delete(key)) ]
else
[ key, value ]
end
end.to_h

custom.deep_merge default
end
end
20 changes: 10 additions & 10 deletions test/dummy/app/views/comboboxes/custom_attrs.html.erb
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
<%= combobox_tag "state", State.all do |combobox| %>
<% combobox.customize :fieldset, class: "custom-class--fieldset", data: { customized_fieldset: "" } %>
<% combobox.customize :hidden_field, class: "custom-class--hidden_field", data: { customized_hidden_field: "" } %>
<% combobox.customize :input, class: "custom-class--input", data: { customized_input: "" } %>
<% combobox.customize :handle, class: "custom-class--handle", data: { customized_handle: "" } %>
<% combobox.customize :listbox, class: "custom-class--listbox", data: { customized_listbox: "" } %>
<% combobox.customize :dialog, class: "custom-class--dialog", data: { customized_dialog: "" } %>
<% combobox.customize :dialog_wrapper, class: "custom-class--dialog_wrapper", data: { customized_dialog_wrapper: "" } %>
<% combobox.customize :dialog_label, class: "custom-class--dialog_label", data: { customized_dialog_label: "" } %>
<% combobox.customize :dialog_input, class: "custom-class--dialog_input", data: { customized_dialog_input: "" } %>
<% combobox.customize :dialog_listbox, class: "custom-class--dialog_listbox", data: { customized_dialog_listbox: "" } %>
<% combobox.customize_fieldset class: "custom-class--fieldset", data: { customized_fieldset: "" } %>
<% combobox.customize_hidden_field class: "custom-class--hidden_field", data: { customized_hidden_field: "" } %>
<% combobox.customize_input class: "custom-class--input", data: { customized_input: "" } %>
<% combobox.customize_handle class: "custom-class--handle", data: { customized_handle: "" } %>
<% combobox.customize_listbox class: "custom-class--listbox", data: { customized_listbox: "" } %>
<% combobox.customize_dialog class: "custom-class--dialog", data: { customized_dialog: "" } %>
<% combobox.customize_dialog_wrapper class: "custom-class--dialog_wrapper", data: { customized_dialog_wrapper: "" } %>
<% combobox.customize_dialog_label class: "custom-class--dialog_label", data: { customized_dialog_label: "" } %>
<% combobox.customize_dialog_input class: "custom-class--dialog_input", data: { customized_dialog_input: "" } %>
<% combobox.customize_dialog_listbox class: "custom-class--dialog_listbox", data: { customized_dialog_listbox: "" } %>
<% end %>
4 changes: 2 additions & 2 deletions test/presenters/hotwire_combobox/component_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class HotwireCombobox::ComponentTest < ApplicationViewTestCase

test "protected attributes cannot be overridden" do
component = HotwireCombobox::Component.new(view, "field-name", id: "id-string")
component.customize :input, id: "foo", name: "bar", role: "baz", value: "qux", aria: { haspopup: "foobar" }, data: { hw_combobox_target: "thud" }
component.customize_input id: "foo", name: "bar", role: "baz", value: "qux", aria: { haspopup: "foobar" }, data: { hw_combobox_target: "thud" }
html = render component

assert_attrs html, tag_name: :input, id: "id-string"
Expand All @@ -29,7 +29,7 @@ class HotwireCombobox::ComponentTest < ApplicationViewTestCase

test "attributes can be customized" do
component = HotwireCombobox::Component.new(view, "field-name", id: "id-string")
component.customize :input, class: "my-custom-class", data: { my_custom_attr: "value" }
component.customize_input class: "my-custom-class", data: { my_custom_attr: "value" }
html = render component

assert_attrs html, tag_name: :input, class: "hw-combobox__input my-custom-class"
Expand Down

0 comments on commit 6a5c98d

Please sign in to comment.