diff --git a/admin/app/components/solidus_admin/ui/forms/field/component.html.erb b/admin/app/components/solidus_admin/ui/forms/field/component.html.erb
new file mode 100644
index 00000000000..28a3811f2fd
--- /dev/null
+++ b/admin/app/components/solidus_admin/ui/forms/field/component.html.erb
@@ -0,0 +1,28 @@
+
diff --git a/admin/app/components/solidus_admin/ui/forms/field/component.rb b/admin/app/components/solidus_admin/ui/forms/field/component.rb
new file mode 100644
index 00000000000..b66c7cc1a97
--- /dev/null
+++ b/admin/app/components/solidus_admin/ui/forms/field/component.rb
@@ -0,0 +1,72 @@
+# frozen_string_literal: true
+
+class SolidusAdmin::UI::Forms::Field::Component < SolidusAdmin::BaseComponent
+ def initialize(label:, hint: nil, tip: nil, error: nil, input_attributes: nil, **attributes)
+ @label = label
+ @hint = hint
+ @tip = tip
+ @error = [error] if error.present?
+ @attributes = attributes
+ @input_attributes = input_attributes
+
+ raise ArgumentError, "provide either a block or input_attributes" if content? && input_attributes
+ end
+
+ def self.text_field(form, method, hint: nil, tip: nil, size: :m, **attributes)
+ errors = form.object.errors.messages_for(method).presence
+
+ new(
+ label: form.object.class.human_attribute_name(method),
+ hint: hint,
+ tip: tip,
+ error: errors,
+ input_attributes: {
+ name: "#{form.object_name}[#{method}]",
+ tag: :input,
+ size: size,
+ value: form.object.public_send(method),
+ error: (errors.to_sentence.capitalize if errors),
+ **attributes,
+ }
+ )
+ end
+
+ def self.select(form, method, choices, hint: nil, tip: nil, size: :m, **attributes)
+ errors = form.object.errors.messages_for(method).presence
+
+ new(
+ label: form.object.class.human_attribute_name(method),
+ hint: hint,
+ tip: tip,
+ error: errors,
+ input_attributes: {
+ name: "#{form.object_name}[#{method}]",
+ tag: :select,
+ choices: choices,
+ size: size,
+ value: form.object.public_send(method),
+ error: (errors.to_sentence.capitalize if errors),
+ **attributes,
+ }
+ )
+ end
+
+ def self.text_area(form, method, hint: nil, tip: nil, size: :m, **attributes)
+ errors = form.object.errors.messages_for(method).presence
+
+ new(
+ label: form.object.class.human_attribute_name(method),
+ hint: hint,
+ tip: tip,
+ error: errors,
+ input_attributes: {
+ name: "#{form.object_name}[#{method}]",
+ size: size,
+ tag: :textarea,
+ value: form.object.public_send(method),
+ error: (errors.to_sentence.capitalize if errors),
+ **attributes,
+ }
+ )
+ end
+end
diff --git a/admin/app/components/solidus_admin/ui/forms/fieldset/component.erb b/admin/app/components/solidus_admin/ui/forms/fieldset/component.erb
deleted file mode 100644
index 7b05c129a8f..00000000000
--- a/admin/app/components/solidus_admin/ui/forms/fieldset/component.erb
+++ /dev/null
@@ -1,4 +0,0 @@
-<%= tag.fieldset(**fieldset_html_attributes) do %>
- <%= legend_and_toggletip_tags %>
- <%= content %>
-<% end %>
diff --git a/admin/app/components/solidus_admin/ui/forms/fieldset/component.rb b/admin/app/components/solidus_admin/ui/forms/fieldset/component.rb
deleted file mode 100644
index 17a32b6710a..00000000000
--- a/admin/app/components/solidus_admin/ui/forms/fieldset/component.rb
+++ /dev/null
@@ -1,64 +0,0 @@
-# frozen_string_literal: true
-
-class SolidusAdmin::UI::Forms::Fieldset::Component < SolidusAdmin::BaseComponent
- # @param legend [String, nil] The legend of the fieldset.
- # @param fieldset_attributes [Hash] Attributes to pass to the fieldset tag.
- # @param legend_attributes [Hash, nil] Attributes to pass to the legend tag.
- # @param toggletip_attributes [Hash, nil] Attributes to pass to a toggletip
- # component that will be rendered after the legend.
- def initialize(
- legend: nil,
- attributes: {},
- legend_attributes: {},
- toggletip_attributes: {}
- )
- @legend = legend
- @attributes = attributes
- @legend_attributes = legend_attributes
- @toggletip_attributes = toggletip_attributes
- end
-
- def fieldset_html_attributes
- {
- class: fieldset_classes,
- **@attributes.except(:class)
- }
- end
-
- def fieldset_classes
- %w[p-6 mb-6 border border-gray-100 rounded-lg] + Array(@attributes[:class]).compact
- end
-
- def legend_and_toggletip_tags
- return "" unless @legend || @toggletip_attributes.any?
-
- tag.div(class: "flex mb-4") do
- legend_tag + toggletip_tag
- end
- end
-
- def legend_tag
- return "".html_safe unless @legend
-
- tag.legend(@legend, **legend_html_attributes)
- end
-
- def legend_html_attributes
- {
- class: legend_classes,
- **@legend_attributes.except(:class)
- }
- end
-
- def legend_classes
- %w[body-title mr-2] + Array(@legend_attributes[:class]).compact
- end
-
- def toggletip_tag
- return "" unless @toggletip_attributes.any?
-
- tag.div do
- render component("ui/toggletip").new(**@toggletip_attributes)
- end
- end
-end
diff --git a/admin/app/components/solidus_admin/ui/forms/form/component.erb b/admin/app/components/solidus_admin/ui/forms/form/component.erb
deleted file mode 100644
index 7ea4458e700..00000000000
--- a/admin/app/components/solidus_admin/ui/forms/form/component.erb
+++ /dev/null
@@ -1,3 +0,0 @@
-<%= form_with(**@attributes) do |builder| %>
- <%= render_elements(@elements, builder) %>
-<% end %>
diff --git a/admin/app/components/solidus_admin/ui/forms/form/component.rb b/admin/app/components/solidus_admin/ui/forms/form/component.rb
deleted file mode 100644
index ca5e2b51742..00000000000
--- a/admin/app/components/solidus_admin/ui/forms/form/component.rb
+++ /dev/null
@@ -1,44 +0,0 @@
-# frozen_string_literal: true
-
-class SolidusAdmin::UI::Forms::Form::Component < SolidusAdmin::BaseComponent
- # @param elements [Array<#call(form, builder)>] Builders of renderable
- # elements within a form context. They need to implement `#call(form,
- # builder)`, where the arguments are an instance of this class and an
- # instance of `ActionView::Helpers::FormBuilder`. The method needs to return
- # something responding to `#render_in(view_context)`. See the following
- # classes for examples:
- # - {SolidusAdmin::Form::Elements::Field}
- # - {SolidusAdmin::Form::Elements::Fieldset}
- # - {SolidusAdmin::Form::Elements::Component}
- # - {SolidusAdmin::Form::Elements::HTML}
- # @param attributes [Hash] Attributes to pass to the Rails `form_with` helper,
- # which is used to render the form.
- def initialize(elements:, **attributes)
- @elements = elements
- @attributes = attributes
- end
-
- # @return [Hash{Symbol => SolidusAdmin::BaseComponent}] Hash of component
- # classes dependencies given on initialization.
- def dependencies
- {
- fieldset: component("ui/forms/fieldset"),
- text_field: component("ui/forms/text_field"),
- text_area: component("ui/forms/text_area")
- }
- end
-
- # @api private
- def render_elements(elements, builder)
- safe_join(
- elements.map do |element|
- render_element(element, builder)
- end
- )
- end
-
- # @api private
- def render_element(element, builder)
- render element.call(self, builder)
- end
-end
diff --git a/admin/app/components/solidus_admin/ui/forms/guidance/component.rb b/admin/app/components/solidus_admin/ui/forms/guidance/component.rb
deleted file mode 100644
index 67d88cb189a..00000000000
--- a/admin/app/components/solidus_admin/ui/forms/guidance/component.rb
+++ /dev/null
@@ -1,70 +0,0 @@
-# frozen_string_literal: true
-
-# @api private
-class SolidusAdmin::UI::Forms::Guidance::Component < SolidusAdmin::BaseComponent
- def initialize(field:, builder:, hint:, errors:, disabled: false)
- @field = field
- @builder = builder
- @hint = hint
- @disabled = disabled
- @errors = errors || @builder.object&.errors || raise(ArgumentError, <<~MSG
- When the form builder is not bound to a model instance, you must pass an
- errors Hash (`{ field_name: [errors] }`) to the component.
- MSG
- )
- end
-
- def call
- return "" unless needed?
-
- tag.div(class: "mt-2") do
- hint_tag + error_tag
- end
- end
-
- def hint_tag
- return "".html_safe unless @hint
-
- tag.p(id: hint_id, class: "body-tiny #{hint_text_color_class}") do
- @hint
- end
- end
-
- def hint_text_color_class
- @disabled ? "text-gray-300" : "text-gray-500"
- end
-
- def hint_id
- "#{prefix}_hint"
- end
-
- def error_tag
- return "".html_safe unless errors?
-
- tag.p(id: error_id, class: "body-tiny text-red-400") do
- @errors[@field].map do |error|
- tag.span(class: "block") { error.capitalize }
- end.reduce(&:+)
- end
- end
-
- def errors?
- @errors[@field].present?
- end
-
- def error_id
- "#{prefix}_error"
- end
-
- def prefix
- "#{@builder.object_name}_#{@field}"
- end
-
- def aria_describedby
- "#{hint_id if @hint} #{error_id if errors?}"
- end
-
- def needed?
- @hint || errors?
- end
-end
diff --git a/admin/app/components/solidus_admin/ui/forms/input/component.js b/admin/app/components/solidus_admin/ui/forms/input/component.js
new file mode 100644
index 00000000000..44ff79b03b0
--- /dev/null
+++ b/admin/app/components/solidus_admin/ui/forms/input/component.js
@@ -0,0 +1,16 @@
+import { Controller } from '@hotwired/stimulus'
+
+export default class extends Controller {
+ static values = {
+ customValidity: String,
+ }
+
+ connect() {
+ if (this.customValidityValue)
+ this.element.setCustomValidity(this.customValidityValue)
+ }
+
+ clearCustomValidity() {
+ this.element.setCustomValidity('')
+ }
+}
diff --git a/admin/app/components/solidus_admin/ui/forms/input/component.rb b/admin/app/components/solidus_admin/ui/forms/input/component.rb
new file mode 100644
index 00000000000..e933cbb9409
--- /dev/null
+++ b/admin/app/components/solidus_admin/ui/forms/input/component.rb
@@ -0,0 +1,99 @@
+# frozen_string_literal: true
+
+class SolidusAdmin::UI::Forms::Input::Component < SolidusAdmin::BaseComponent
+ SIZES = {
+ s: "form-control-sm px-3 py-1.5 body-small",
+ m: "form-control-md px-3 py-1.5 body-small",
+ l: "form-control-lg px-3 py-1.5 body-text"
+ }.freeze
+
+ HEIGHTS = {
+ s: "h-7",
+ m: "h-9",
+ l: "h-12"
+ }.freeze
+
+ MULTILINE_HEIGHTS = {
+ s: %w[min-h-[84px]],
+ m: %w[min-h-[108px]],
+ l: %w[min-h-[144px]],
+ }.freeze
+
+ TYPES = Set.new(%i[
+ text
+ password
+ number
+ email
+ tel
+ url
+ search
+ color
+ date
+ datetime-local
+ month
+ week
+ time
+ ]).freeze
+
+ def initialize(tag: :input, size: :m, error: nil, **attributes)
+ raise ArgumentError, "unsupported tag: #{tag}" unless %i[input textarea select].include?(tag)
+
+ specialized_classes = []
+
+ case tag
+ when :input
+ specialized_classes << "form-input"
+ specialized_classes << HEIGHTS[size]
+ if attributes[:type] && !TYPES.include?(attributes[:type])
+ raise ArgumentError, "unsupported type attribute: #{attributes[:type]}"
+ end
+ when :textarea
+ specialized_classes << "form-textarea"
+ specialized_classes << MULTILINE_HEIGHTS[size]
+ when :select
+ if attributes[:multiple]
+ specialized_classes << "form-multiselect"
+ specialized_classes << MULTILINE_HEIGHTS[size]
+ else
+ specialized_classes << "form-select"
+ 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"
+ specialized_classes << HEIGHTS[size]
+ end
+ end
+
+ attributes[:class] = [
+ %w[
+ w-full
+ text-black bg-white border border-gray-300 rounded-sm placeholder:text-gray-400
+ hover:border-gray-500
+ focus:ring focus:ring-gray-300 focus:ring-0.5 focus:bg-white focus:ring-offset-0 [&:focus-visible]:outline-none
+ disabled:bg-gray-50 disabled:text-gray-500 disabled:placeholder:text-gray-300 disabled:cursor-not-allowed
+ invalid:border-red-400 invalid:hover:border-red-400 invalid:text-red-400
+ aria-invalid:border-red-400 aria-invalid:hover:border-red-400 aria-invalid:text-red-400
+ ],
+ SIZES[size],
+ specialized_classes,
+ attributes[:class],
+ ].compact.join(" ")
+
+ @tag = tag
+ @size = size
+ @error = error
+ @attributes = attributes
+ end
+
+ def call
+ if @tag == :select && @attributes[:choices]
+ with_content options_for_select(@attributes.delete(:choices), @attributes.delete(:value))
+ end
+
+ tag.public_send(
+ @tag,
+ content,
+ "data-controller": stimulus_id,
+ "data-#{stimulus_id}-custom-validity-value": @error.presence,
+ "data-action": "#{stimulus_id}#clearCustomValidity",
+ **@attributes
+ )
+ end
+end
diff --git a/admin/app/components/solidus_admin/ui/forms/label/component.rb b/admin/app/components/solidus_admin/ui/forms/label/component.rb
deleted file mode 100644
index 072042cc811..00000000000
--- a/admin/app/components/solidus_admin/ui/forms/label/component.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-# frozen_string_literal: true
-
-# @api private
-class SolidusAdmin::UI::Forms::Label::Component < SolidusAdmin::BaseComponent
- def initialize(field:, builder:)
- @field = field
- @builder = builder
- end
-
- def call
- @builder.label(@field, class: "block mb-0.5 body-tiny-bold")
- end
-end
diff --git a/admin/app/components/solidus_admin/ui/forms/select/component.js b/admin/app/components/solidus_admin/ui/forms/select/component.js
deleted file mode 100644
index b8e937cdfb5..00000000000
--- a/admin/app/components/solidus_admin/ui/forms/select/component.js
+++ /dev/null
@@ -1,33 +0,0 @@
-import { Controller } from '@hotwired/stimulus'
-
-export default class extends Controller {
- static targets = ['select', 'arrow']
- static classes = ['regular', 'prompt', 'arrowPrompt']
-
- connect () {
- this.addClassToOptions()
- this.refreshSelectClass()
- }
-
- // Add class to all the options to avoid inheriting the select's styles
- addClassToOptions () {
- this.selectTarget.querySelectorAll('option').forEach((option) => {
- if (option.value == '') {
- option.classList.add(this.promptClass)
- } else {
- option.classList.add(this.regularClass)
- }
- })
- }
-
- // Make the select look like a placeholder when the prompt is selected
- refreshSelectClass () {
- if (this.selectTarget.options[this.selectTarget.selectedIndex].value == '') {
- this.selectTarget.classList.add(this.promptClass)
- this.arrowTarget.classList.add(this.arrowPromptClass)
- } else {
- this.selectTarget.classList.remove(this.promptClass)
- this.arrowTarget.classList.remove(this.arrowPromptClass)
- }
- }
-}
diff --git a/admin/app/components/solidus_admin/ui/forms/select/component.rb b/admin/app/components/solidus_admin/ui/forms/select/component.rb
deleted file mode 100644
index b52669184a3..00000000000
--- a/admin/app/components/solidus_admin/ui/forms/select/component.rb
+++ /dev/null
@@ -1,162 +0,0 @@
-# frozen_string_literal: true
-
-class SolidusAdmin::UI::Forms::Select::Component < SolidusAdmin::BaseComponent
- SIZES = {
- s: {
- select: %w[leading-4 body-small],
- arrow: %w[w-4 h-4]
- },
- m: {
- select: %w[leading-6 body-small],
- arrow: %w[w-5 h-5]
- },
- l: {
- select: %w[leading-9 body-text],
- arrow: %w[w-6 h-6]
- }
- }.freeze
-
- # @param field [Symbol] the name of the field. Usually a model attribute.
- # @param builder [ActionView::Helpers::FormBuilder] the form builder instance.
- # @param size [Symbol] the size of the field: `:s`, `:m` or `:l`.
- # @param choices [Array] an array of choices for the select box. All the
- # formats valid for Rails' `select` helper are supported.
- # @param hint [String, null] helper text to display below the select box.
- # @param errors [Hash, nil] a Hash of errors for the field. If `nil` and the
- # builder is bound to a model instance, the component will automatically fetch
- # the errors from the model.
- # @param options [Hash] additional options to pass to Rails' `select` helper.
- # @param attributes [Hash] additional HTML attributes to add to the select box.
- # @raise [ArgumentError] when the form builder is not bound to a model
- # instance and no `errors` Hash is passed to the component.
- def initialize(
- field:,
- builder:,
- size: :m,
- choices: [],
- hint: nil,
- errors: nil,
- toggletip: nil,
- options: {},
- attributes: {}
- )
- @field = field
- @builder = builder
- @size = size
- @choices = choices
- @hint = hint
- @toggletip = toggletip
- @options = options
- @attributes = HashWithIndifferentAccess.new(attributes)
- @errors = errors
- end
-
- def call
- guidance = component("ui/forms/guidance").new(
- field: @field,
- builder: @builder,
- hint: @hint,
- errors: @errors,
- disabled: @attributes[:disabled]
- )
-
- toggletip_tag = @toggletip.present? ? render(component('ui/toggletip').new(text: @toggletip)) : ''
-
- tag.div(class: "mb-6") do
- tag.div(label_tag + toggletip_tag, class: "flex gap-1") + field_tag(guidance) + guidance_tag(guidance)
- end
- end
-
- def field_wrapper_tag(guidance)
- tag.div(
- class: "relative",
- "data-controller" => stimulus_id,
- "data-#{stimulus_id}-regular-class" => "text-black",
- "data-#{stimulus_id}-prompt-class" => "text-gray-400",
- "data-#{stimulus_id}-arrow-prompt-class" => "!fill-gray-500"
- ) do
- field_tag(guidance) + arrow_tag(guidance)
- end
- end
-
- def field_tag(guidance)
- @builder.select(
- @field,
- @choices,
- @options,
- class: field_classes(guidance),
- **field_aria_describedby_attribute(guidance),
- **field_error_attributes(guidance),
- **@attributes.except(:class).merge(
- "data-#{stimulus_id}-target" => "select",
- "data-action" => "#{stimulus_id}#refreshSelectClass"
- )
- )
- end
-
- def field_classes(guidance)
- %w[
- block px-3 py-1.5 w-full
- appearance-none
- text-black
- bg-white border border-gray-300 rounded-sm
- hover:border-gray-500
- focus:border-gray-500 focus:shadow-[0_0_0_2px_#bbb] focus-visible:outline-none
- disabled:bg-gray-50 disabled:text-gray-300
- ] + field_size_classes + field_error_classes(guidance) + Array(@attributes[:class]).compact
- end
-
- def field_size_classes
- SIZES.fetch(@size)[:select]
- end
-
- def field_error_classes(guidance)
- return [] unless guidance.errors?
-
- %w[border-red-400 text-red-400]
- end
-
- def field_aria_describedby_attribute(guidance)
- return {} unless guidance.needed?
-
- {
- "aria-describedby": guidance.aria_describedby
- }
- end
-
- def field_error_attributes(guidance)
- return {} unless guidance.errors?
-
- {
- "aria-invalid": true
- }
- end
-
- def arrow_tag(guidance)
- icon_tag(
- "arrow-down-s-fill",
- class: SIZES.fetch(@size)[:arrow] + [
- arrow_color_class(guidance)
- ] + %w[absolute right-3 top-1/2 translate-y-[-50%] pointer-events-none],
- "data-#{stimulus_id}-target" => "arrow"
- )
- end
-
- def arrow_color_class(guidance)
- if @attributes[:disabled]
- "fill-gray-500"
- elsif guidance.errors?
- "fill-red-400"
- else
- "fill-gray-700"
- end
- end
-
- def label_tag
- render component("ui/forms/label").new(field: @field, builder: @builder)
- end
-
- def guidance_tag(guidance)
- render guidance
- end
-end
diff --git a/admin/app/components/solidus_admin/ui/forms/text_area/component.rb b/admin/app/components/solidus_admin/ui/forms/text_area/component.rb
deleted file mode 100644
index 4921633b8ae..00000000000
--- a/admin/app/components/solidus_admin/ui/forms/text_area/component.rb
+++ /dev/null
@@ -1,105 +0,0 @@
-# frozen_string_literal: true
-
-class SolidusAdmin::UI::Forms::TextArea::Component < SolidusAdmin::BaseComponent
- SIZES = {
- s: %w[h-20 body-small],
- m: %w[h-28 body-small],
- l: %w[h-36 body-text]
- }.freeze
-
- # @param field [Symbol] the name of the field. Usually a model attribute.
- # @param builder [ActionView::Helpers::FormBuilder] the form builder instance.
- # @param size [Symbol] the size of the field: `:s`, `:m` or `:l`.
- # @param hint [String, null] helper text to display below the field.
- # @param errors [Hash, nil] a Hash of errors for the field. If `nil` and the
- # builder is bound to a model instance, the component will automatically fetch
- # the errors from the model.
- # @param attributes [Hash] additional HTML attributes to add to the field.
- # @raise [ArgumentError] when the form builder is not bound to a model
- # instance and no `errors` Hash is passed to the component.
- def initialize(
- field:,
- builder:,
- size: :m,
- hint: nil,
- errors: nil,
- **attributes
- )
- @field = field
- @builder = builder
- @size = size
- @hint = hint
- @attributes = HashWithIndifferentAccess.new(attributes)
- @errors = errors
- end
-
- def call
- guidance = component("ui/forms/guidance").new(
- field: @field,
- builder: @builder,
- hint: @hint,
- errors: @errors,
- disabled: @attributes[:disabled]
- )
-
- tag.div(class: "mb-6") do
- label_tag + field_tag(guidance) + guidance_tag(guidance)
- end
- end
-
- def field_tag(guidance)
- @builder.text_area(
- @field,
- class: field_classes(guidance),
- **field_aria_describedby_attribute(guidance),
- **field_error_attributes(guidance),
- **@attributes.except(:class)
- )
- end
-
- def field_classes(guidance)
- %w[
- block px-3 py-4 w-full
- text-black
- bg-white border border-gray-300 rounded-sm
- hover:border-gray-500
- placeholder:text-gray-400
- focus:border-gray-500 focus:shadow-[0_0_0_2px_#bbb] focus-visible:outline-none
- disabled:bg-gray-50 disabled:text-gray-300
- ] + field_size_classes + field_error_classes(guidance) + Array(@attributes[:class]).compact
- end
-
- def field_size_classes
- SIZES.fetch(@size)
- end
-
- def field_aria_describedby_attribute(guidance)
- return {} unless guidance.needed?
-
- {
- "aria-describedby": guidance.aria_describedby
- }
- end
-
- def field_error_classes(guidance)
- return [] unless guidance.errors?
-
- %w[border-red-400 text-red-400]
- end
-
- def field_error_attributes(guidance)
- return {} unless guidance.errors?
-
- {
- "aria-invalid": true
- }
- end
-
- def label_tag
- render component("ui/forms/label").new(field: @field, builder: @builder)
- end
-
- def guidance_tag(guidance)
- render guidance
- end
-end
diff --git a/admin/app/components/solidus_admin/ui/forms/text_field/component.rb b/admin/app/components/solidus_admin/ui/forms/text_field/component.rb
deleted file mode 100644
index e33a31c13eb..00000000000
--- a/admin/app/components/solidus_admin/ui/forms/text_field/component.rb
+++ /dev/null
@@ -1,136 +0,0 @@
-# frozen_string_literal: true
-
-class SolidusAdmin::UI::Forms::TextField::Component < SolidusAdmin::BaseComponent
- SIZES = {
- s: %w[leading-4 body-small],
- m: %w[leading-6 body-small],
- l: %w[leading-9 body-text]
- }.freeze
-
- TYPES = {
- color: :color_field,
- date: :date_field,
- datetime: :datetime_field,
- email: :email_field,
- month: :month_field,
- number: :number_field,
- password: :password_field,
- phone: :phone_field,
- range: :range_field,
- search: :search_field,
- text: :text_field,
- time: :time_field,
- url: :url_field,
- week: :week_field
- }.freeze
-
- # @param field [Symbol] the name of the field. Usually a model attribute.
- # @param builder [ActionView::Helpers::FormBuilder] the form builder instance.
- # @param type [Symbol] the type of the field. Defaults to `:text`.
- # @param size [Symbol] the size of the field: `:s`, `:m` or `:l`.
- # @param hint [String, null] helper text to display below the field.
- # @param errors [Hash, nil] a Hash of errors for the field. If `nil` and the
- # builder is bound to a model instance, the component will automatically fetch
- # the errors from the model.
- # @param attributes [Hash] additional HTML attributes to add to the field.
- # @raise [ArgumentError] when the form builder is not bound to a model
- # instance and no `errors` Hash is passed to the component.
- def initialize(
- field:,
- builder:,
- type: :text,
- size: :m,
- hint: nil,
- errors: nil,
- toggletip: nil,
- **attributes
- )
- @field = field
- @builder = builder
- @type = type
- @size = size
- @hint = hint
- @type = type
- @toggletip = toggletip
- @attributes = HashWithIndifferentAccess.new(attributes)
- @errors = errors
- end
-
- def call
- guidance = component("ui/forms/guidance").new(
- field: @field,
- builder: @builder,
- hint: @hint,
- errors: @errors,
- disabled: @attributes[:disabled]
- )
-
- toggletip_tag = @toggletip.present? ? render(component('ui/toggletip').new(text: @toggletip)) : ''
-
- tag.div(class: "w-full mb-6") do
- tag.div(label_tag + toggletip_tag, class: "flex gap-1") + field_tag(guidance) + guidance_tag(guidance)
- end
- end
-
- def field_tag(guidance)
- @builder.send(
- field_helper,
- @field,
- class: field_classes(guidance),
- **field_aria_describedby_attribute(guidance),
- **field_error_attributes(guidance),
- **@attributes.except(:class)
- )
- end
-
- def field_classes(guidance)
- %w[
- form-input
- block px-3 py-1.5 w-full
- text-black
- bg-white border border-gray-300 rounded-sm
- hover:border-gray-500
- placeholder:text-gray-400
- focus:border-gray-500 focus:shadow-[0_0_0_2px_#bbb] focus-visible:outline-none
- disabled:bg-gray-50 disabled:text-gray-300
- ] + field_size_classes + field_error_classes(guidance) + Array(@attributes[:class]).compact
- end
-
- def field_helper
- TYPES.fetch(@type)
- end
-
- def field_size_classes
- SIZES.fetch(@size)
- end
-
- def field_aria_describedby_attribute(guidance)
- return {} unless guidance.needed?
-
- {
- "aria-describedby": guidance.aria_describedby
- }
- end
-
- def field_error_classes(guidance)
- return [] unless guidance.errors?
-
- %w[border-red-400 text-red-400]
- end
-
- def field_error_attributes(guidance)
- return {} unless guidance.errors?
-
- {
- "aria-invalid": true
- }
- end
-
- def label_tag
- render component("ui/forms/label").new(field: @field, builder: @builder)
- end
-
- def guidance_tag(guidance)
- render guidance
- end
-end
diff --git a/admin/app/components/solidus_admin/ui/panel/component.html.erb b/admin/app/components/solidus_admin/ui/panel/component.html.erb
index 065507a2655..17c3c2a4c0e 100644
--- a/admin/app/components/solidus_admin/ui/panel/component.html.erb
+++ b/admin/app/components/solidus_admin/ui/panel/component.html.erb
@@ -23,7 +23,7 @@
<% end %>
<% if content&.present? %>
-
+
<%= content %>
<% end %>
diff --git a/admin/config/solidus_admin/tailwind.config.js.erb b/admin/config/solidus_admin/tailwind.config.js.erb
index 8846b6f8ca7..5cf74942e64 100644
--- a/admin/config/solidus_admin/tailwind.config.js.erb
+++ b/admin/config/solidus_admin/tailwind.config.js.erb
@@ -74,6 +74,8 @@ module.exports = {
},
backgroundImage: {
'arrow-right-up-line': "url('solidus_admin/arrow_right_up_line.svg')",
+ 'arrow-down-s-fill-gray-700': "url('solidus_admin/arrow_down_s_fill_gray_700.svg')",
+ 'arrow-down-s-fill-red-400': "url('solidus_admin/arrow_down_s_fill_red_400.svg')",
},
boxShadow: {
sm: '0px 1px 2px 0px rgba(0, 0, 0, 0.04)',
diff --git a/admin/lib/solidus_admin/form/element/component.rb b/admin/lib/solidus_admin/form/element/component.rb
deleted file mode 100644
index c46751808ab..00000000000
--- a/admin/lib/solidus_admin/form/element/component.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-# frozen_string_literal: true
-
-module SolidusAdmin
- module Form
- module Element
- # Builds an arbitrary component in a form context.
- #
- # This class can be used to render an arbitrary components in a form.
- #
- # This is useful when there's the need to render a component that's not
- # strictly related to a form definition, but still needs to be within the
- # form tags.
- class Component
- # @param component [ViewComponent::Base] the component instance to
- # render.
- def initialize(component:)
- @component = component
- end
-
- # @api private
- def call(_form, _builder)
- @component
- end
- end
- end
- end
-end
diff --git a/admin/lib/solidus_admin/form/element/field.rb b/admin/lib/solidus_admin/form/element/field.rb
deleted file mode 100644
index 9b54f22679e..00000000000
--- a/admin/lib/solidus_admin/form/element/field.rb
+++ /dev/null
@@ -1,45 +0,0 @@
-# frozen_string_literal: true
-
-module SolidusAdmin
- module Form
- module Element
- # Builds a form field component.
- #
- # This class encapsulates a form field definition and its resolution to a
- # component.
- class Field
- # @param component [Symbol, ViewComponent::Base] the component to be
- # used when rendering. It can be a component class (which needs to
- # accept the `builder:` parameter on initialization) or a Symbol. When
- # the latter, it's used to infer the one configured in the form
- # instance. For instance, for a `:text_field` type, the component used
- # will be the one given to the form component as the
- # `text_field_component` keyword argument on initialization.
- # @param attributes [Hash] attributes to pass to the field component.
- def initialize(component:, **attributes)
- @component = component
- @attributes = attributes
- end
-
- # @api private
- def call(form, builder)
- component_class(form).new(
- builder: builder,
- **@attributes
- )
- end
-
- private
-
- def component_class(form)
- case @component
- when Symbol
- form.dependencies[@component]
- else
- @component
- end
- end
- end
- end
- end
-end
diff --git a/admin/lib/solidus_admin/form/element/fieldset.rb b/admin/lib/solidus_admin/form/element/fieldset.rb
deleted file mode 100644
index 08f01e4674d..00000000000
--- a/admin/lib/solidus_admin/form/element/fieldset.rb
+++ /dev/null
@@ -1,47 +0,0 @@
-# frozen_string_literal: true
-
-module SolidusAdmin
- module Form
- module Element
- # Builds a form fieldset component.
- #
- # This class encapsulates a form fieldset definition and its resolution to
- # a component.
- class Fieldset
- # @param elements [Array<#call(form, builder)>] See
- # {SolidusAdmin::UI::Forms::Form::Component#initialize}.
- # @param component [ViewComponent::Base, nil] the component to be
- # used when rendering. When `nil`, the component configured in the form
- # `fieldset_component` keyword argument on initialization is used.
- # @param attributes [Hash] Attributes to pass to the fieldset
- # component.
- def initialize(elements:, component: nil, **attributes)
- @elements = elements
- @component = component
- @attributes = attributes
- end
-
- # @api private
- def call(form, builder)
- component_class(form).new(
- **@attributes
- ).with_content(
- render_elements(form, builder)
- )
- end
-
- private
-
- def component_class(form)
- @component || form.dependencies[:fieldset]
- end
-
- def render_elements(form, builder)
- return "" if @elements.empty?
-
- form.render_elements(@elements, builder)
- end
- end
- end
- end
-end
diff --git a/admin/lib/solidus_admin/form/element/html.rb b/admin/lib/solidus_admin/form/element/html.rb
deleted file mode 100644
index e9c681507b2..00000000000
--- a/admin/lib/solidus_admin/form/element/html.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-# frozen_string_literal: true
-
-module SolidusAdmin
- module Form
- module Element
- # Builds arbitrary HTML in a form.
- #
- # This class can be used to render arbitrary content in a form.
- #
- # This is useful when there's the need to render content that's not
- # strictly related to a form definition, but still needs to be within the
- # form tags. If the content is a component, it's better to use
- # {SolidusAdmin::Form::Element::Component} instead.
- class HTML
- # @param html [String] the HTML to render.
- def initialize(html:)
- @html = html
- end
-
- # @api private
- def call(_form, _builder)
- self
- end
-
- # @api private
- def render_in(_view_context)
- @html
- end
- end
- end
- end
-end
diff --git a/admin/spec/components/previews/solidus_admin/ui/forms/field/component_preview.rb b/admin/spec/components/previews/solidus_admin/ui/forms/field/component_preview.rb
new file mode 100644
index 00000000000..38aa05b4e0c
--- /dev/null
+++ b/admin/spec/components/previews/solidus_admin/ui/forms/field/component_preview.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+# @component "ui/forms/field"
+class SolidusAdmin::UI::Forms::Field::ComponentPreview < ViewComponent::Preview
+ include SolidusAdmin::Preview
+
+ def overview
+ render_with_template
+ end
+
+ # @param hint text
+ # @param tip text
+ # @param error text
+ def playground(label: "My field", hint: "hint", tip: "tip", error: "error")
+ render component("ui/forms/field").new(label: label, hint: hint, tip: tip, error: error, input_attributes: {
+ tag: :input, value: "My value", error: error
+ })
+ end
+end
diff --git a/admin/spec/components/previews/solidus_admin/ui/forms/field/component_preview/overview.html.erb b/admin/spec/components/previews/solidus_admin/ui/forms/field/component_preview/overview.html.erb
new file mode 100644
index 00000000000..b47ea5c14bc
--- /dev/null
+++ b/admin/spec/components/previews/solidus_admin/ui/forms/field/component_preview/overview.html.erb
@@ -0,0 +1,26 @@
+
+
+ <%= render current_component.new(
+ label: "Greeting",
+ hint: "A brief explanation",
+ tip: "More in-detail information about this filed",
+ error: "The second letter should be lowecase",
+ ) do %>
+ <%= render component('ui/forms/input').new(value: "HEllo world!", error: "The second letter should be lowecase") %>
+ <% end %>
+
diff --git a/admin/spec/components/previews/solidus_admin/ui/forms/fieldset/component_preview.rb b/admin/spec/components/previews/solidus_admin/ui/forms/fieldset/component_preview.rb
deleted file mode 100644
index 98ba84cb3f7..00000000000
--- a/admin/spec/components/previews/solidus_admin/ui/forms/fieldset/component_preview.rb
+++ /dev/null
@@ -1,47 +0,0 @@
-# frozen_string_literal: true
-
-# @component "ui/forms/fieldset"
-class SolidusAdmin::UI::Forms::Fieldset::ComponentPreview < ViewComponent::Preview
- include SolidusAdmin::Preview
-
- # The fieldset component is used to render a set of fields in a form.
- #
- # Most commonly, it'll be used indirectly through the definition given to a
- # [form component](../form/overview).
- #
- # For standalone usage, it wraps the yielded content in a fieldset tag:
- #
- # ```erb
- # <%= render components('ui/forms/fieldset').new do %>
- # <%= # ... %>
- # <% end %>
- # ```
- #
- # The legend of the fieldset can be set with the `legend` option:
- #
- # ```erb
- # <%= render components('ui/forms/fieldset').new(
- # legend: "My fieldset"
- # ) do %>
- # <%= # ... %>
- # <% end %>
- # ```
- #
- # Lastly, a toggletip can be added to the legend with the
- # `toggletip_attributes`, which will be passed to the [toggletip
- # component](../../toggletip):
- #
- # ```erb
- # <%= render components('ui/forms/fieldset').new(
- # legend: "My fieldset",
- # toggletip_attributes: {
- # text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
- # position: :right
- # }
- # ) do %>
- # <%= # ... %>
- # <% end %>
- # ```
- def overview
- end
-end
diff --git a/admin/spec/components/previews/solidus_admin/ui/forms/fieldset/component_preview/overview.html.erb b/admin/spec/components/previews/solidus_admin/ui/forms/fieldset/component_preview/overview.html.erb
deleted file mode 100644
index 8376955d631..00000000000
--- a/admin/spec/components/previews/solidus_admin/ui/forms/fieldset/component_preview/overview.html.erb
+++ /dev/null
@@ -1,48 +0,0 @@
-<%= form_with(url: "#", scope: :overview, method: :get, class: "w-full") do |form| %>
-
-<% end %>
diff --git a/admin/spec/components/previews/solidus_admin/ui/forms/form/component_preview.rb b/admin/spec/components/previews/solidus_admin/ui/forms/form/component_preview.rb
deleted file mode 100644
index 10786cf5a41..00000000000
--- a/admin/spec/components/previews/solidus_admin/ui/forms/form/component_preview.rb
+++ /dev/null
@@ -1,170 +0,0 @@
-# frozen_string_literal: true
-
-require "solidus_admin/form/element/field"
-require "solidus_admin/form/element/fieldset"
-require "solidus_admin/form/element/component"
-require "solidus_admin/form/element/html"
-
-# @component "ui/forms/form"
-class SolidusAdmin::UI::Forms::Form::ComponentPreview < ViewComponent::Preview
- include SolidusAdmin::Preview
-
- # The form component is used to render a form tag along with its content, most
- # commonly form fields.
- #
- # Internally, the
- # [`form_with`](https://edgeapi.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-form_with)
- # Rails helper is used to render the form tag, and the component will dispatch
- # given arguments to it.
- #
- # The definition of the form is provided from the outside through the
- # `elements` parameter. This parameter is an array of builders of renderable
- # elements, and Solidus Admin provides all the necessary ones to build a form
- # following its UI:
- #
- # ## SolidusAdmin::Form::Element::Field
- #
- # This element renders a form field:
- #
- # ```erb
- # <%=
- # render components('ui/forms/form',
- # model: Spree::Product.new,
- # elements: [
- # SolidusAdmin::Form::Element::Field.new(
- # component: :text_field,
- # field: :name
- # )
- # ]
- # )
- # %>
- # ```
- #
- # The previous example will use the [`text_field`
- # component](../text_field/overview), but you can use any of the available
- # field component.
- #
- # ## SolidusAdmin::Form::Element::Fieldset
- #
- # Wraps a set of fields in a fieldset.
- #
- # You need to provide the inner fields akin to how it's done with the form
- # component. [The fieldet component](../fieldset/overview) is used under the
- # hood, and you can pass any of its attributes through the `attributes`
- # parameter.
- #
- # ```erb
- # <%=
- # render components('ui/forms/form',
- # model: Spree::Product.new,
- # elements: [
- # SolidusAdmin::Form::Element::Fieldset.new(
- # elements: [
- # SolidusAdmin::Form::Element::Field.new(
- # component: :text_field,
- # field: :name
- # )
- # ],
- # legend: "Product details",
- # toggletip_attributes: { text: "Minimal info", position: :right }
- # )
- # ]
- # )
- # %>
- # ```
- #
- # ## SolidusAdmin::Form::Element::Component
- #
- # This element allows you to render any component inside the form.
- #
- # ```erb
- # <%=
- # render components('ui/forms/form',
- # model: Spree::Product.new,
- # elements: [
- # SolidusAdmin::Form::Element::Component.new(
- # component: MyCustomComponent.new
- # )
- # ]
- # )
- # %>
- # ```
- #
- # ## SolidusAdmin::Form::Element::HTML
- #
- # This element allows you to render any HTML inside the form.
- #
- # ```erb
- # <%=
- # render components('ui/forms/form',
- # model: Spree::Product.new,
- # elements: [
- # SolidusAdmin::Form::Element::HTML.new(
- # html: "
-<% end %>
diff --git a/admin/spec/components/previews/solidus_admin/ui/forms/text_field/component_preview/playground.html.erb b/admin/spec/components/previews/solidus_admin/ui/forms/text_field/component_preview/playground.html.erb
deleted file mode 100644
index 76072bee203..00000000000
--- a/admin/spec/components/previews/solidus_admin/ui/forms/text_field/component_preview/playground.html.erb
+++ /dev/null
@@ -1,15 +0,0 @@
-<%= form_with(url: "#", scope: :playground, method: :get, class: "w-56") do |form| %>
- <%=
- render current_component.new(
- builder: form,
- size: size,
- type: type,
- field: field,
- value: value,
- hint: hint,
- errors: errors,
- placeholder: placeholder,
- disabled: disabled
- )
- %>
-<% end %>
diff --git a/admin/spec/components/solidus_admin/ui/forms/form/component_spec.rb b/admin/spec/components/solidus_admin/ui/forms/field/component_spec.rb
similarity index 63%
rename from admin/spec/components/solidus_admin/ui/forms/form/component_spec.rb
rename to admin/spec/components/solidus_admin/ui/forms/field/component_spec.rb
index 87d5382ba0c..e62c3020a13 100644
--- a/admin/spec/components/solidus_admin/ui/forms/form/component_spec.rb
+++ b/admin/spec/components/solidus_admin/ui/forms/field/component_spec.rb
@@ -2,7 +2,7 @@
require "spec_helper"
-RSpec.describe SolidusAdmin::UI::Forms::Form::Component, type: :component do
+RSpec.describe SolidusAdmin::UI::Forms::Field::Component, type: :component do
it "renders the overview preview" do
render_preview(:overview)
end
diff --git a/admin/spec/components/solidus_admin/ui/forms/fieldset/component_spec.rb b/admin/spec/components/solidus_admin/ui/forms/fieldset/component_spec.rb
deleted file mode 100644
index cfb51bf07e9..00000000000
--- a/admin/spec/components/solidus_admin/ui/forms/fieldset/component_spec.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-# frozen_string_literal: true
-
-require "spec_helper"
-
-RSpec.describe SolidusAdmin::UI::Forms::Fieldset::Component, type: :component do
- it "renders the overview preview" do
- render_preview(:overview)
- end
-end
diff --git a/admin/spec/components/solidus_admin/ui/forms/guidance/component_spec.rb b/admin/spec/components/solidus_admin/ui/forms/guidance/component_spec.rb
deleted file mode 100644
index f5092e16c0d..00000000000
--- a/admin/spec/components/solidus_admin/ui/forms/guidance/component_spec.rb
+++ /dev/null
@@ -1,37 +0,0 @@
-# frozen_string_literal: true
-
-require "spec_helper"
-
-RSpec.describe SolidusAdmin::UI::Forms::Guidance::Component, type: :component do
- describe "#initialize" do
- it "uses given errors when form is bound to a model" do
- form = double("form", object: double("model", errors: {}))
-
- component = described_class.new(builder: form, field: :name, hint: nil, errors: { name: ["can't be blank"] })
-
- expect(component.errors?).to be(true)
- end
-
- it "uses model errors when form is bound to a model and they are not given" do
- form = double("form", object: double("model", errors: { name: ["can't be blank"] }))
-
- component = described_class.new(builder: form, field: :name, hint: nil, errors: nil)
-
- expect(component.errors?).to be(true)
- end
-
- it "uses given errors when form is not bound to a model" do
- form = double("form", object: nil)
-
- component = described_class.new(builder: form, field: :name, hint: nil, errors: { name: ["can't be blank"] })
-
- expect(component.errors?).to be(true)
- end
-
- it "raises an error when form is not bound to a model and errors are not given" do
- form = double("form", object: nil)
-
- expect { described_class.new(builder: form, field: :name, errors: nil) }.to raise_error(ArgumentError)
- end
- end
-end
diff --git a/admin/spec/components/solidus_admin/ui/forms/input/component_spec.rb b/admin/spec/components/solidus_admin/ui/forms/input/component_spec.rb
new file mode 100644
index 00000000000..cb00d6a65cb
--- /dev/null
+++ b/admin/spec/components/solidus_admin/ui/forms/input/component_spec.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+require "spec_helper"
+
+RSpec.describe SolidusAdmin::UI::Forms::Input::Component, type: :component do
+ it "renders the overview preview" do
+ render_preview(:overview)
+ render_preview(:input_playground)
+ render_preview(:select_playground)
+ render_preview(:textarea_playground)
+ end
+
+ it "only accepts certain 'type' attributes for the input" do
+ expect {
+ render_inline(described_class.new(type: :button))
+ }.to raise_error(ArgumentError, /unsupported type attribute: button/)
+ end
+
+ describe "with `tag: input`" do
+ it "renders a text input" do
+ render_inline(described_class.new(type: :text, name: "name", value: "value"))
+
+ expect(page).to have_css("input[type='text'][name='name'][value='value']")
+ end
+
+ it "renders a password input" do
+ render_inline(described_class.new(type: :password, name: "name", value: "value"))
+
+ expect(page).to have_css("input[type='password'][name='name'][value='value']")
+ end
+
+ it "renders a number input" do
+ render_inline(described_class.new(type: :number, name: "name", value: "value"))
+
+ expect(page).to have_css("input[type='number'][name='name'][value='value']")
+ end
+ end
+end
diff --git a/admin/spec/components/solidus_admin/ui/forms/select/component_spec.rb b/admin/spec/components/solidus_admin/ui/forms/select/component_spec.rb
deleted file mode 100644
index 1cc630faec1..00000000000
--- a/admin/spec/components/solidus_admin/ui/forms/select/component_spec.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-# frozen_string_literal: true
-
-require "spec_helper"
-
-RSpec.describe SolidusAdmin::UI::Forms::Select::Component, type: :component do
- it "renders the overview preview" do
- render_preview(:overview)
- end
-
- it "renders the playground preview" do
- render_preview(:playground)
- end
-end
diff --git a/admin/spec/components/solidus_admin/ui/forms/text_area/component_spec.rb b/admin/spec/components/solidus_admin/ui/forms/text_area/component_spec.rb
deleted file mode 100644
index af5e6f1bf09..00000000000
--- a/admin/spec/components/solidus_admin/ui/forms/text_area/component_spec.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-# frozen_string_literal: true
-
-require "spec_helper"
-
-RSpec.describe SolidusAdmin::UI::Forms::TextArea::Component, type: :component do
- it "renders the overview preview" do
- render_preview(:overview)
- end
-
- it "renders the playground preview" do
- render_preview(:playground)
- end
-end
diff --git a/admin/spec/components/solidus_admin/ui/forms/text_field/component_spec.rb b/admin/spec/components/solidus_admin/ui/forms/text_field/component_spec.rb
deleted file mode 100644
index 2d89dabfe54..00000000000
--- a/admin/spec/components/solidus_admin/ui/forms/text_field/component_spec.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-# frozen_string_literal: true
-
-require "spec_helper"
-
-RSpec.describe SolidusAdmin::UI::Forms::TextField::Component, type: :component do
- it "renders the overview preview" do
- render_preview(:overview)
- end
-
- it "renders the playground preview" do
- render_preview(:playground)
- end
-end
diff --git a/admin/spec/solidus_admin/form/element/component_spec.rb b/admin/spec/solidus_admin/form/element/component_spec.rb
deleted file mode 100644
index eda06035b50..00000000000
--- a/admin/spec/solidus_admin/form/element/component_spec.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-# frozen_string_literal: true
-
-require "spec_helper"
-require "solidus_admin/form/element/component"
-
-RSpec.describe SolidusAdmin::Form::Element::Component do
- describe "#call" do
- it "returns the given instance component" do
- element = described_class.new(component: :component)
-
- expect(
- element.call(double("form"), double("builder"))
- ).to be(:component)
- end
- end
-end
diff --git a/admin/spec/solidus_admin/form/element/field_spec.rb b/admin/spec/solidus_admin/form/element/field_spec.rb
deleted file mode 100644
index 131ab0251cc..00000000000
--- a/admin/spec/solidus_admin/form/element/field_spec.rb
+++ /dev/null
@@ -1,71 +0,0 @@
-# frozen_string_literal: true
-
-require "spec_helper"
-require "solidus_admin/form/element/field"
-
-RSpec.describe SolidusAdmin::Form::Element::Field do
- include SolidusAdmin::ComponentHelpers
-
- describe "#call" do
- it "returns an instance of the given component" do
- component = mock_component do
- def initialize(builder:); end
- end
- builder = double("builder")
-
- element = described_class.new(component: component)
-
- expect(
- element.call(double("form"), builder)
- ).to be_a(component)
- end
-
- it "initializes the component with the given attributes" do
- component = mock_component do
- attr_reader :builder, :attributes
-
- def initialize(builder:, **attributes)
- @builder = builder
- @attributes = attributes
- end
- end
- attributes = { foo: :bar }
- element = described_class.new(component: component, **attributes)
-
- result = element.call(double("form"), double("builder"))
-
- expect(result.attributes).to eq(attributes)
- end
-
- it "initializes the component with the given builder" do
- component = mock_component do
- attr_reader :builder
-
- def initialize(builder:)
- @builder = builder
- end
- end
- builder = double("builder")
- element = described_class.new(component: component)
-
- result = element.call(double("form"), builder)
-
- expect(result.builder).to be(builder)
- end
-
- it "infers the component class from the form dependencies when given as a Symbol" do
- text_field_component_class = mock_component do
- def initialize(builder:); end
- end
- element = described_class.new(component: :text_field)
- component = SolidusAdmin::UI::Forms::Form::Component.new(elements: [element])
-
- allow(component).to receive(:component).and_call_original
- allow(component).to receive(:component).with('ui/forms/text_field').and_return(text_field_component_class)
-
- result = element.call(component, double("builder"))
-
- expect(result).to be_a(text_field_component_class)
- end
- end
-end
diff --git a/admin/spec/solidus_admin/form/element/fieldset_spec.rb b/admin/spec/solidus_admin/form/element/fieldset_spec.rb
deleted file mode 100644
index c7938db84aa..00000000000
--- a/admin/spec/solidus_admin/form/element/fieldset_spec.rb
+++ /dev/null
@@ -1,54 +0,0 @@
-# frozen_string_literal: true
-
-require "spec_helper"
-require "solidus_admin/form/element/fieldset"
-require "solidus_admin/form/element/html"
-
-RSpec.describe SolidusAdmin::Form::Element::Fieldset do
- include SolidusAdmin::ComponentHelpers
-
- describe "#call" do
- it "returns an instance of the given component" do
- component = mock_component
- element = described_class.new(component: component, elements: [])
-
- expect(
- element.call(double("form"), double("builder"))
- ).to be_a(component)
- end
-
- it "initializes the component with the given attributes" do
- component = mock_component do
- attr_reader :attributes
-
- def initialize(**attributes)
- @attributes = attributes
- end
- end
- attributes = { foo: :bar }
- element = described_class.new(component: component, elements: [], **attributes)
-
- result = element.call(double("form"), double("builder"))
-
- expect(result.attributes).to eq(attributes)
- end
-
- it "gives the concatenation of the rendered elements as the content of the component" do
- component = mock_component
- elements = [
- SolidusAdmin::Form::Element::HTML.new(html: "foo"),
- SolidusAdmin::Form::Element::HTML.new(html: "bar")
- ]
- element = described_class.new(component: component, elements: elements)
- form = SolidusAdmin::UI::Forms::Form::Component.new(elements: [element])
-
- # Workaround for view_context not being available in specs
- expect(form).to receive(:render_element).with(elements[0], any_args).and_return("foo")
- expect(form).to receive(:render_element).with(elements[1], any_args).and_return("bar")
-
- result = element.call(form, double("builder"))
-
- expect(result.content).to eq("foobar")
- end
- end
-end
diff --git a/admin/spec/solidus_admin/form/element/html_spec.rb b/admin/spec/solidus_admin/form/element/html_spec.rb
deleted file mode 100644
index e6c0a7de0e8..00000000000
--- a/admin/spec/solidus_admin/form/element/html_spec.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-# frozen_string_literal: true
-
-require "spec_helper"
-require "solidus_admin/form/element/html"
-
-RSpec.describe SolidusAdmin::Form::Element::HTML do
- describe "#call" do
- it "returns itself" do
- element = described_class.new(html: "foo")
-
- expect(
- element.call(double("form"), double("builder"))
- ).to be(element)
- end
- end
-
- describe "#render_in" do
- it "returns the given HTML" do
- element = described_class.new(html: "foo")
-
- expect(
- element.render_in(double("view_context"))
- ).to eq("foo")
- end
- end
-end