Skip to content

Commit 12faa88

Browse files
authored
Merge pull request #6225 from chaimann/admin-refactor-address-component-v2
Refactor address form component (properly this time)
2 parents 5c415e7 + e45cec2 commit 12faa88

File tree

18 files changed

+292
-80
lines changed

18 files changed

+292
-80
lines changed
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# frozen_string_literal: true
2+
3+
# ViewComponent v3 provided experimental functionality to define default content for slots
4+
# https://viewcomponent.org/guide/slots.html#default_slot_name, but unfortunately
5+
# it did not quite work: https://github.com/ViewComponent/view_component/issues/2169.
6+
# Good news the issue has been resolved, not so good news - it's targeted to be released in v4,
7+
# so we have to patch the functionality until we upgrade.
8+
# The solution has been copied from here https://github.com/ViewComponent/view_component/pull/2291/files.
9+
10+
require "view_component/version"
11+
if Gem::Version.new(ViewComponent::VERSION::STRING) >= Gem::Version.new("4")
12+
raise "The fix is included in ViewComponent v4, please remove this patch #{__FILE__}"
13+
end
14+
15+
module SolidusAdmin
16+
module SlotableDefault
17+
def get_slot(slot_name)
18+
@__vc_set_slots ||= {}
19+
content unless content_evaluated? # ensure content is loaded so slots will be defined
20+
21+
# If the slot is set, return it
22+
return @__vc_set_slots[slot_name] if @__vc_set_slots[slot_name]
23+
24+
# If there is a default method for the slot, call it
25+
if (default_method = registered_slots[slot_name][:default_method])
26+
renderable_value = send(default_method)
27+
slot = ViewComponent::Slot.new(self)
28+
29+
if renderable_value.respond_to?(:render_in)
30+
slot.__vc_component_instance = renderable_value
31+
else
32+
slot.__vc_content = renderable_value
33+
end
34+
35+
slot
36+
elsif self.class.registered_slots[slot_name][:collection]
37+
# If empty slot is a collection, return an empty array
38+
[]
39+
end
40+
end
41+
end
42+
end
Lines changed: 1 addition & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,7 @@
11
<fieldset class="<%= stimulus_id %>"
2-
data-controller="<%= stimulus_id %>"
32
<%= :disabled if @disabled %>
43
>
54
<div class="<%= stimulus_id %>--address-form flex flex-wrap gap-4 pb-4">
6-
<%= render component("ui/forms/field").text_field(@form_field_name, :name, object: @addressable) if @include_name_field %>
7-
<%= render component("ui/forms/field").text_field(@form_field_name, :address1, object: @addressable) %>
8-
<%= render component("ui/forms/field").text_field(@form_field_name, :address2, object: @addressable) %>
9-
<div class="flex gap-4 w-full">
10-
<%= render component("ui/forms/field").text_field(@form_field_name, :city, object: @addressable) %>
11-
<%= render component("ui/forms/field").text_field(@form_field_name, :zipcode, object: @addressable) %>
12-
</div>
13-
14-
<%= render component("ui/forms/field").select(
15-
@form_field_name,
16-
:country_id,
17-
Spree::Country.all.map { |c| [c.name, c.id] },
18-
object: @addressable,
19-
value: @addressable.try(:country_id),
20-
"data-#{stimulus_id}-target": "country",
21-
"data-action": "change->#{stimulus_id}#loadStates"
22-
) %>
23-
24-
<%= content_tag(:div,
25-
data: { "#{stimulus_id}-target": "stateNameWrapper" },
26-
class: (@addressable.country&.states&.empty? ? "flex flex-col gap-2 w-full" : "hidden flex flex-col gap-2 w-full")
27-
) do %>
28-
<%= render component("ui/forms/field").text_field(
29-
@form_field_name,
30-
:state_name,
31-
object: @addressable,
32-
value: @addressable.try(:state_name),
33-
"data-#{stimulus_id}-target": "stateName"
34-
) %>
35-
<% end %>
36-
<input autocomplete="off" type="hidden" name=<%= "#{@form_field_name}[state_id]" %>>
37-
38-
<%= content_tag(:div,
39-
data: { "#{stimulus_id}-target": "stateWrapper" },
40-
class: (@addressable.country&.states&.empty? ? "hidden flex flex-col gap-2 w-full" : "flex flex-col gap-2 w-full")
41-
) do %>
42-
<%= render component("ui/forms/field").select(
43-
@form_field_name,
44-
:state_id,
45-
state_options,
46-
object: @addressable,
47-
value: @addressable.try(:state_id),
48-
"data-#{stimulus_id}-target": "state"
49-
) %>
50-
<% end %>
51-
52-
<%= render component("ui/forms/field").text_field(@form_field_name, :phone, object: @addressable) %>
53-
<%= render component("ui/forms/field").text_field(@form_field_name, :email, object: @addressable) %>
54-
<% if Spree::Backend::Config.show_reverse_charge_fields %>
55-
<%= render component("ui/forms/field").text_field(@form_field_name, :vat_id, object: @addressable) %>
56-
<%= render component("ui/forms/field").select(
57-
@form_field_name,
58-
:reverse_charge_status,
59-
Spree::Address.reverse_charge_statuses.keys.map { |key| [I18n.t("spree.reverse_charge_statuses.#{key}"), key] },
60-
object: @addressable
61-
) %>
62-
<% end %>
5+
<%= fieldset %>
636
</div>
647
</fieldset>
Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,37 @@
11
# frozen_string_literal: true
22

33
class SolidusAdmin::UI::Forms::Address::Component < SolidusAdmin::BaseComponent
4-
def initialize(addressable:, form_field_name:, disabled: false, include_name_field: true)
5-
@addressable = addressable
6-
@form_field_name = form_field_name
4+
DefaultNamedFieldsetNotFound = Class.new(NameError)
5+
6+
include SolidusAdmin::SlotableDefault
7+
8+
renders_one :fieldset
9+
10+
# @param fieldset [Symbol] use a default named fieldset, component of the same name must be defined
11+
# in "ui/forms/address/fieldsets"
12+
# @param extends [Array<Symbol, Hash{Symbol => #call}>] extend default fieldset,
13+
# see +SolidusAdmin::UI::Forms::Address::Fieldsets::Base+
14+
# @param excludes [Array<Symbol>, Symbol] optionally exclude fields that are present in a default fieldset
15+
# @raise [DefaultNamedFieldsetNotFound] if the provided +:fieldset+ option does not correspond to a defined component
16+
# in "ui/forms/address/fieldsets"
17+
def initialize(addressable:, form_field_name:, disabled: false, fieldset: :contact, extends: [], excludes: [])
718
@disabled = disabled
8-
@include_name_field = include_name_field
19+
@default_fieldset = fieldset_component(fieldset).new(
20+
addressable:,
21+
form_field_name:,
22+
extends:,
23+
excludes:,
24+
)
925
end
1026

11-
def state_options
12-
return [] unless @addressable.country
13-
@addressable.country.states.map { |s| [s.name, s.id] }
27+
attr_reader :default_fieldset
28+
29+
private
30+
31+
def fieldset_component(fieldset)
32+
component("ui/forms/address/fieldsets/#{fieldset}")
33+
rescue SolidusAdmin::ComponentRegistry::ComponentNotFoundError
34+
raise DefaultNamedFieldsetNotFound,
35+
"to use a default named fieldset `#{fieldset}` you must implement a component in 'ui/forms/address/fieldsets/#{fieldset}'"
1436
end
1537
end
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<div class="flex gap-4 w-full">
2+
<%= render component("ui/forms/field").text_field(@form_field_name, :city, object: @addressable) %>
3+
<%= render component("ui/forms/field").text_field(@form_field_name, :zipcode, object: @addressable) %>
4+
</div>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# frozen_string_literal: true
2+
3+
class SolidusAdmin::UI::Forms::Address::Fields::CityAndZipcode::Component < SolidusAdmin::BaseComponent
4+
def initialize(addressable:, form_field_name:)
5+
@addressable = addressable
6+
@form_field_name = form_field_name
7+
end
8+
end
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<div class="flex flex-col gap-4 w-full" data-controller="<%= stimulus_id %>">
2+
<%= render component("ui/forms/field").select(
3+
@form_field_name,
4+
:country_id,
5+
Spree::Country.all.map { |c| [c.name, c.id] },
6+
object: @addressable,
7+
value: @addressable.try(:country_id),
8+
"data-#{stimulus_id}-target": "country",
9+
"data-action": "change->#{stimulus_id}#loadStates"
10+
) %>
11+
12+
<%= content_tag(:div,
13+
data: { "#{stimulus_id}-target": "stateNameWrapper" },
14+
class: (@addressable.country&.states&.empty? ? "flex flex-col gap-2 w-full" : "hidden flex flex-col gap-2 w-full")
15+
) do %>
16+
<%= render component("ui/forms/field").text_field(
17+
@form_field_name,
18+
:state_name,
19+
object: @addressable,
20+
value: @addressable.try(:state_name),
21+
"data-#{stimulus_id}-target": "stateName"
22+
) %>
23+
<% end %>
24+
<input autocomplete="off" type="hidden" name=<%= "#{@form_field_name}[state_id]" %>>
25+
26+
<%= content_tag(:div,
27+
data: { "#{stimulus_id}-target": "stateWrapper" },
28+
class: (@addressable.country&.states&.empty? ? "hidden flex flex-col gap-2 w-full" : "flex flex-col gap-2 w-full")
29+
) do %>
30+
<%= render component("ui/forms/field").select(
31+
@form_field_name,
32+
:state_id,
33+
state_options,
34+
object: @addressable,
35+
value: @addressable.try(:state_id),
36+
"data-#{stimulus_id}-target": "state"
37+
) %>
38+
<% end %>
39+
</div>

admin/app/components/solidus_admin/ui/forms/address/component.js renamed to admin/app/components/solidus_admin/ui/forms/address/fields/country_and_state/component.js

File renamed without changes.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# frozen_string_literal: true
2+
3+
class SolidusAdmin::UI::Forms::Address::Fields::CountryAndState::Component < SolidusAdmin::BaseComponent
4+
def initialize(addressable:, form_field_name:)
5+
@addressable = addressable
6+
@form_field_name = form_field_name
7+
end
8+
9+
private
10+
11+
def state_options
12+
return [] unless @addressable.country
13+
@addressable.country.states.map { |s| [s.name, s.id] }
14+
end
15+
end
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<%= render component("ui/forms/field").text_field(@form_field_name, :vat_id, object: @addressable) %>
2+
<%= render component("ui/forms/field").select(
3+
@form_field_name,
4+
:reverse_charge_status,
5+
Spree::Address.reverse_charge_statuses.keys.map { |key| [I18n.t("spree.reverse_charge_statuses.#{key}"), key] },
6+
object: @addressable
7+
) %>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# frozen_string_literal: true
2+
3+
class SolidusAdmin::UI::Forms::Address::Fields::ReverseChargeFields::Component < SolidusAdmin::BaseComponent
4+
def initialize(addressable:, form_field_name:)
5+
@addressable = addressable
6+
@form_field_name = form_field_name
7+
end
8+
9+
def render?
10+
Spree::Backend::Config.show_reverse_charge_fields
11+
end
12+
end

0 commit comments

Comments
 (0)