Skip to content

Commit 63d01f1

Browse files
authored
Merge pull request #6190 from chaimann/admin-ui-select-component
[Admin][UI] New select component
2 parents 7901f79 + 252788d commit 63d01f1

File tree

25 files changed

+404
-87
lines changed

25 files changed

+404
-87
lines changed

admin/app/components/solidus_admin/ui/forms/address/component.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,5 +54,7 @@ export default class extends Controller {
5454
option.innerText = state.name
5555
stateSelect.appendChild(option)
5656
})
57+
58+
stateSelect.setAttribute("synced", "false");
5759
}
5860
}

admin/app/components/solidus_admin/ui/forms/field/component.html.erb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@
2020
<div class="
2121
font-normal text-xs
2222
[:disabled~&]:text-gray-300 text-gray-500
23-
flex gap-1 flex-col
23+
flex gap-1 flex-col [&>.error]:hidden [&>.error]:peer-invalid:block
2424
">
2525
<%= tag.span @hint if @hint.present? %>
26-
<%= tag.span safe_join(@error, tag.br), class: "text-red-600" if @error.present? %>
26+
<%= tag.span safe_join(@error, tag.br), class: "error text-red-600" if @error.present? %>
2727
</div>
2828
<% end %>
2929
</label>

admin/app/components/solidus_admin/ui/forms/field/component.rb

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -36,20 +36,16 @@ def self.text_field(form, method, object: nil, hint: nil, tip: nil, size: :m, **
3636
def self.select(form, method, choices, object: nil, hint: nil, tip: nil, size: :m, **attributes)
3737
object_name, object, label, errors = extract_form_details(form, object, method)
3838

39-
new(
39+
component("ui/forms/select").new(
4040
label:,
4141
hint:,
4242
tip:,
43-
error: errors,
44-
input_attributes: {
45-
name: "#{object_name}[#{method}]",
46-
tag: :select,
47-
choices:,
48-
size:,
49-
value: (object.public_send(method) if object.respond_to?(method)),
50-
error: (errors.to_sentence.capitalize if errors),
51-
**attributes,
52-
}
43+
size: size,
44+
name: "#{object_name}[#{method}]",
45+
choices:,
46+
value: (object.public_send(method) if object.respond_to?(method)),
47+
error: (errors.to_sentence.capitalize if errors),
48+
**attributes
5349
)
5450
end
5551

admin/app/components/solidus_admin/ui/forms/input/component.js

Lines changed: 0 additions & 15 deletions
This file was deleted.

admin/app/components/solidus_admin/ui/forms/input/component.rb

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ class SolidusAdmin::UI::Forms::Input::Component < SolidusAdmin::BaseComponent
3737
]).freeze
3838

3939
def initialize(tag: :input, size: :m, error: nil, **attributes)
40-
raise ArgumentError, "unsupported tag: #{tag}" unless %i[input textarea select].include?(tag)
40+
raise ArgumentError, "unsupported tag: #{tag}" unless %i[input textarea].include?(tag)
4141

4242
specialized_classes = []
4343
readonly_classes = "read-only:bg-gray-15 focus:read-only:bg-gray-15 focus:read-only:ring-0
@@ -56,20 +56,11 @@ def initialize(tag: :input, size: :m, error: nil, **attributes)
5656
specialized_classes << "form-textarea"
5757
specialized_classes << readonly_classes
5858
specialized_classes << MULTILINE_HEIGHTS[size]
59-
when :select
60-
if attributes[:multiple]
61-
specialized_classes << "form-multiselect"
62-
specialized_classes << MULTILINE_HEIGHTS[size]
63-
else
64-
specialized_classes << "form-select"
65-
specialized_classes << "bg-arrow-down-s-fill-gray-700 invalid:bg-arrow-down-s-fill-red-400 aria-invalid:bg-arrow-down-s-fill-red-400"
66-
specialized_classes << HEIGHTS[size]
67-
end
6859
end
6960

7061
attributes[:class] = [
7162
%w[
72-
w-full
63+
peer w-full
7364
text-black bg-white border border-gray-300 rounded-sm placeholder:text-gray-400
7465
hover:border-gray-500
7566
focus:ring focus:ring-gray-300 focus:ring-0.5 focus:bg-white focus:ring-offset-0 [&:focus-visible]:outline-none
@@ -89,9 +80,7 @@ def initialize(tag: :input, size: :m, error: nil, **attributes)
8980
end
9081

9182
def call
92-
if @tag == :select && @attributes[:choices]
93-
with_content options_for_select(@attributes.delete(:choices), @attributes.delete(:value))
94-
elsif @tag == :textarea && @attributes[:value]
83+
if @tag == :textarea && @attributes[:value]
9584
with_content @attributes.delete(:value)
9685
end
9786

@@ -109,9 +98,8 @@ def build_tag
10998

11099
def tag_options
111100
@tag_options ||= {
112-
"data-controller": stimulus_id,
113-
"data-#{stimulus_id}-custom-validity-value": @error.presence,
114-
"data-action": "#{stimulus_id}#clearCustomValidity",
101+
"data-controller": "custom-validity",
102+
"data-custom-validity-error-message-value": @error.presence,
115103
**@attributes
116104
}
117105
end
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<div class="flex flex-col gap-2 w-full">
2+
<div class="flex gap-1 items-center">
3+
<label class="text-gray-700 font-semibold text-xs" <%= tag.attributes for: @attributes[:id] %>>
4+
<%= @label %>
5+
</label>
6+
<%= render component('ui/toggletip').new(text: @tip) if @tip.present? %>
7+
</div>
8+
9+
<%= tag.select(options_for_select(@choices, @selected), **@attributes) %>
10+
11+
<% if @hint.present? || @error.present? %>
12+
<div class="
13+
font-normal text-xs
14+
[:disabled~&]:text-gray-300 text-gray-500
15+
flex gap-1 flex-col [&>.error]:hidden [&>.error]:peer-invalid:block
16+
">
17+
<%= tag.span @hint if @hint.present? %>
18+
<%= tag.span safe_join(@error, tag.br), class: "error text-red-600" %>
19+
</div>
20+
<% end %>
21+
</div>
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# frozen_string_literal: true
2+
3+
class SolidusAdmin::UI::Forms::Select::Component < SolidusAdmin::BaseComponent
4+
FONT_SIZES = {
5+
s: "[&>.control]:text-xs [&_.dropdown]:text-xs",
6+
m: "[&>.control]:text-sm [&_.dropdown]:text-sm",
7+
l: "[&>.control]:text-base [&_.dropdown]:text-base",
8+
}.freeze
9+
10+
HEIGHTS = {
11+
control: {
12+
s: "[&>.control]:min-h-7",
13+
m: "[&>.control]:min-h-9",
14+
l: "[&>.control]:min-h-12",
15+
},
16+
option: {
17+
s: "[&_.option]:h-7",
18+
m: "[&_.option]:h-9",
19+
l: "[&_.option]:h-12",
20+
},
21+
item: {
22+
s: "[&_.item]:h-5",
23+
m: "[&_.item]:h-5.5",
24+
l: "[&_.item]:h-8",
25+
},
26+
}.freeze
27+
28+
def initialize(label:, name:, choices:, size: :m, hint: nil, tip: nil, error: nil, **attributes)
29+
@label = label
30+
@name = name
31+
@hint = hint
32+
@tip = tip
33+
@error = Array.wrap(error)
34+
35+
@choices = choices
36+
@selected = attributes.delete(:value)
37+
38+
@attributes = attributes
39+
@attributes[:name] = @name
40+
@attributes[:is] = "solidus-select"
41+
@attributes[:id] ||= "#{stimulus_id}_#{@name}"
42+
@attributes[:"data-error-message"] = @error.presence
43+
44+
general_classes = ["w-full relative text-black font-normal #{FONT_SIZES[size]}"]
45+
control_classes = ["[&>.control]:peer-invalid:border-red-600 [&>.control]:peer-invalid:hover:border-red-600
46+
[&>.control]:peer-invalid:text-red-600 [&>.control]:flex [&>.control]:flex-wrap [&>.control]:items-center
47+
[&>.control]:gap-1 [&>.control]:rounded-sm [&>.control]:w-full [&>.control]:rounded-sm [&>.control]:pl-3
48+
[&>.control]:pr-10 [&>.control]:py-1.5 [&>.control]:bg-white [&>.control]:border [&>.control]:border-gray-300
49+
[&>.control]:hover:border-gray-500 [&>.control]:has-[:disabled]:bg-gray-50 [&>.control]:has-[:disabled]:text-gray-500
50+
[&>.control]:has-[:disabled]:cursor-not-allowed [&>.control]:has-[:disabled]:hover:border-gray-300
51+
[&>.control]:has-[:focus]:ring [&>.control]:has-[:focus]:ring-gray-300 [&>.control]:has-[:focus]:ring-0.5
52+
[&>.control]:has-[:focus]:bg-white [&>.control]:has-[:focus]:ring-offset-0 [&>.control]:has-[:focus]:outline-none
53+
#{HEIGHTS[:control][size]}"]
54+
55+
unless @attributes[:multiple]
56+
control_classes << "[&>.control]:peer-invalid:bg-arrow-down-s-fill-red-400 [&>.control]:form-select
57+
[&>.control]:bg-arrow-down-s-fill-gray-700"
58+
end
59+
60+
item_classes = []
61+
if @attributes[:multiple]
62+
item_classes << "[&_.item]:flex [&_.item]:gap-1 [&_.item]:items-center [&_.item]:rounded-full [&_.item]:whitespace-nowrap
63+
[&_.item]:px-2 [&_.item]:py-0.5 [&_.item]:bg-graphite-light [&_.item]:peer-invalid:bg-red-100 #{HEIGHTS[:item][size]}
64+
[&_.item_.remove-button]:text-xl [&_.item_.remove-button]:pb-0.5 [&_.item_.remove-button]:order-first
65+
[&_.item_.remove-button]:has-[:disabled]:cursor-not-allowed"
66+
end
67+
68+
input_classes = ["[&_input]:has-[.item]:placeholder:invisible [&_input:disabled]:cursor-not-allowed [&_input:disabled]:bg-gray-50
69+
[&_input]:peer-invalid:placeholder:text-red-400"]
70+
71+
unless @attributes[:multiple]
72+
input_classes << "[&_input]:has-[.item]:opacity-0 [&_input]:has-[.item]:cursor-default"
73+
end
74+
75+
dropdown_classes = ["[&_.dropdown]:w-full [&_.dropdown]:absolute [&_.dropdown]:border [&_.dropdown]:border-gray-100
76+
[&_.dropdown]:mt-0.5 [&_.dropdown]:min-w-[10rem] [&_.dropdown]:p-2 [&_.dropdown]:rounded-sm [&_.dropdown]:z-10
77+
[&_.dropdown]:shadow-lg [&_.dropdown]:bg-white"]
78+
79+
dropdown_content_classes = ["[&_.dropdown-content]:flex [&_.dropdown-content]:flex-col [&_.dropdown-content]:max-h-[200px]
80+
[&_.dropdown-content]:overflow-x-hidden [&_.dropdown-content]:overflow-y-auto [&_.dropdown-content]:scroll-smooth [&_.no-results]:text-gray-500"]
81+
82+
option_classes = ["[&_.option]:p-2 [&_.option]:rounded-sm [&_.option]:min-w-fit [&_.option.active]:bg-gray-50
83+
[&_.option.active]:text-gray-700 [&_.option_.highlight]:bg-yellow [&_.option_.highlight]:rounded-[1px]
84+
#{HEIGHTS[:option][size]}"]
85+
86+
@attributes[:class] = [
87+
"peer",
88+
general_classes,
89+
control_classes,
90+
item_classes,
91+
input_classes,
92+
dropdown_classes,
93+
dropdown_content_classes,
94+
option_classes,
95+
@attributes[:class]
96+
].compact.join(" ")
97+
end
98+
end

admin/app/components/solidus_admin/ui/modal/component.html.erb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
</form>
2424
</header>
2525

26-
<div class="p-4 overflow-auto">
26+
<div class="p-4">
2727
<%= content %>
2828
</div>
2929

admin/app/controllers/solidus_admin/store_credits_controller.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ def ensure_store_credit_reason
202202
@store_credit_reason = Spree::StoreCreditReason.find_by(id: permitted_resource_params[:store_credit_reason_id])
203203

204204
if @store_credit_reason.blank?
205-
@store_credit.errors.add(:store_credit_reason_id, "Store Credit reason must be provided")
205+
@store_credit.errors.add(:store_credit_reason_id, "Store credit reason must be provided")
206206
yield if block_given? # Block is for error template rendering on a per-action basis so this can be re-used.
207207
return false
208208
end
@@ -213,7 +213,7 @@ def ensure_store_credit_category
213213
@store_credit_category = Spree::StoreCreditCategory.find_by(id: permitted_resource_params[:category_id])
214214

215215
if @store_credit_category.blank?
216-
@store_credit.errors.add(:category_id, "Store Credit category must be provided")
216+
@store_credit.errors.add(:category_id, "Store credit category must be provided")
217217
yield if block_given? # Block is for error template rendering on a per-action basis so this can be re-used.
218218
return false
219219
end
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
import "@hotwired/turbo-rails"
2+
import "vendor/custom_elements"
23
import "solidus_admin/controllers"
4+
import "solidus_admin/web_components/solidus_select"

0 commit comments

Comments
 (0)