forked from solidusio/solidus
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Introducing a form component brings a two-fold benefit: 1. It wraps all the form component dependencies in a single place so that we don't need to redeclare them in every other form (e.g., the product creation form). 2. It allows defining a form from the outside, which is a first step towards making the admin forms configurable. Internally, it uses Rails' `form_with` helper [1], and all the given arguments are passed to it. The only exception is the `elements:` key, which is used to define the form elements. The form elements are simple builders of renderable things that happen within a form context. That's to say, to be rendered, both the form component instance and the Rails' form builder are given to a `#call` method. We ship with out-of-the-box form elements for fields, fieldsets and arbitrary components or HTML. Notice that there's a circular dependency between form and form elements. While the form calls the elements to get the renderable content, the elements can use the form to get the configured dependencies. For instance, when rendering a text field element, it'll use the configured text_field dependency in the form. This complexity is an acceptable trade-off for a double benefit: the ability to change all the form dependencies at once (e.g., using a different text field component across a form), and the removal of boilerplate from the form element definitions. Nonetheless, the default behavior can be overridden by passing a different component to the form element. Having access to the form component is also used by the fieldset element to render the nested elements. This commit also makes some tangential changes that were necessary: - We rename the `form:` parameter to `builder:` in the components within the form namespace. As we have now a form component, that makes it more explicit that it corresponds to the Rails' form builder. - We add `.rb` files within the preview directories to Tailwind watchable files. - We modify the `mock_component` test helper to accept not providing a block. [1] - https://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-form_with Ref. solidusio#5329
- Loading branch information
1 parent
7a15fe1
commit a0b26bd
Showing
27 changed files
with
615 additions
and
39 deletions.
There are no files selected for viewing
3 changes: 3 additions & 0 deletions
3
admin/app/components/solidus_admin/ui/forms/form/component.erb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
<%= form_with(**@attributes) do |builder| %> | ||
<%= render_elements(@elements, builder) %> | ||
<% end %> |
53 changes: 53 additions & 0 deletions
53
admin/app/components/solidus_admin/ui/forms/form/component.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
# 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:, | ||
fieldset_component: component("ui/forms/fieldset"), | ||
text_field_component: component("ui/forms/text_field"), | ||
text_area_component: component("ui/forms/text_area"), | ||
**attributes | ||
) | ||
@elements = elements | ||
@fieldset_component = fieldset_component | ||
@text_field_component = text_field_component | ||
@text_area_component = text_area_component | ||
@attributes = attributes | ||
end | ||
|
||
# @return [Hash{Symbol => SolidusAdmin::BaseComponent}] Hash of component | ||
# classes dependencies given on initialization. | ||
def dependencies | ||
{ | ||
fieldset: @fieldset_component, | ||
text_field: @text_field_component, | ||
text_area: @text_area_component | ||
} | ||
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
# 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
# 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
# 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
# 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.