Creating your Own Text Field Component
Brut's Brut::FrontEnd::Components::Input::InputTag
creates only the <input>
HTML element. You will likely want something more sophsticated. You can achieve this by creating your own component.
Feature
We'll make a text field that has a label, error messages, and styling. It will support three sizes: small, normal, and large.
It will require a form and an input name, and optional index as well.
Recipe
Create the Initializer
First, we'll create the component:
bin/scaffold component text_field
Now, edit the initializer to accept the parameters we need:
# app/src/front_end/components/text_field_component.rb
class TextFieldComponent < AppComponent
def initialize(form:,
input_name:,
index: 0, # default for non-array values
size: :normal)
@form = form
@input_name = input_name
@index = index
@size = size
end
end
Outline the HTML
We'll want HTML like so:
<label>
<input ...>
<span>LABEL HERE</span>
<brut-cv-messages></brut-cv-messages>
</label>
Before we worry about CSS or styling, let's sketch this out in view_template
. The actual label text will come from our I18n setup. We'll assume a "labels" top-level section that has sections for each form and then inside that, each input name:
# app/config/i18n/en/2_app.rb
{
"labels": {
"LoginForm": {
"email": "Email addressed you used when singing up",
"password": "Your password",
}
}
}
# app/src/front_end/components/text_field_component.rb
class TextFieldComponent < AppComponent
include Brut::FrontEnd::Components
private attr_reader :form, :input_name, :index
def view_template
label do
InputTag(form:, input_name:, index: )
span { raw(t([ "labels", form.class.name, input_name ])) }
ConstraintViolations(form:, input_name:, index:)
end
end
end
Styling the Component
Styling can happen in a few ways. For simplicity, we'll use CSS and have minimal classes on our HTML. Since the structure is all inside a <label>
, we'll add a class on that, named for our component. We'll also include form and input names in the class to allow overriding if needed. Lastly, we'll include the size as well.
# app/src/front_end/components/text_field_component.rb
class TextFieldComponent < AppComponent
include Brut::FrontEnd::Components
private attr_reader :form, :input_name, :index
def view_template
label_classes = [
class.name,
class.name + "-#{@size}",
form.class.name,
input_name
]
label(class: label_classes) do
InputTag(form:, input_name:, index: )
span { raw(t([ "labels", form.class.name, input_name ])) }
ConstraintViolations(form:, input_name:, index:)
end
end
end
Let's create app/src/front_end/css/TextFieldComponent.css
, which we'll need to @import
:
/* app/src/front_end/css/index.css */
@import "TextFieldComponent.css";
The CSS will assume BrutCSS's design system is available:
/* app/src/front_end/css/TextFieldComponent.css */
.TextFieldComponent {
label {
display: flex;
flex-direction: column;
gap: var(--sp-2);
}
input {
border: solid thin var(--gray-500);
border-radius: var(--br-3);
padding: var(--sp-2);
font-size: var(--fs-3);
}
span {
font-size: var(--fs-2);
font-stye: italic;
color: var(--gray-400);
}
/** We assume the general styling for brut-form
and brut-cv exists in index.css */
brut-cv {
color: red;
}
&.TextFieldComponent-small {
input {
font-size: var(--fs-1);
}
}
&.TextFieldComponent-large {
input {
font-size: var(--fs-4);
}
}
}
Using the Component
To use this component, we can create an instance and send it to Phlex's render
:
def view_template
brut_form do
FormTag(for: @form) do
render TextFieldComponent.new(form: @form,
input_name: :name)
render TextFieldComponent.new(form: @form,
input_name: :quantity,
size: :small)
button { "Save" }
end
end
end