superglue
superglue copied to clipboard
Form helpers for Superglue
Rail's form helpers are an incredible way to quickly build forms from backend objects. I'd like to have some parity on the superglue side so quickly build uncontrolled components on the React side without using RailsTag
Something like this:
json.form_props form_with_props(
url: onboarding_income_index_path,
model: current_user.profile,
method: :post,
scope: :profile, local: true) do |f|
f.number_field :email, max_length: 5
f.number_field :password
f.number_field :password_confirmation
end
{
.....
email: {
input: { type: "email", maxLength: 5}
}
}
then on the Javascript side:
<form {...formProps}>
<label {...formProps.email.label} />
<input {...formProps.email.input}/>
<label {...formProps.password.label}>
<input {...formProps.password.input}>
<RailsForm.Field {...formProps.passwordConfirmation} />
<input type="submit" />
</RailsForm>
We leave how to structure the form up to the user so they can do this if they want in only html:
<label {...formProps.email.label}>
<input {...formProps.email.input}/>
</label>
@jho406 here's where I left off:
require "active_support/core_ext/string"
require "rspec"
# TODO: form_with(model: nil, scope: nil, url: nil, format: nil, **options, &block)
def form_props(url: nil, scope: nil, **options)
builder = MyFormBuilder.new(url: url, scope: scope, **options)
yield builder
builder.to_h
end
class MyFormBuilder
attr_reader :scope
def initialize(url: nil, scope: nil, **options)
@scope = scope
@output = {
accept_charset: "UTF-8",
action: url || "/",
method: options [:method] || "post",
elements: {}
}
end
def text_field(method, options = {})
@output[:elements][scope ? "#{scope}_#{method}".to_sym : method.to_s.to_sym] = {
type: "text",
label: method.to_s.humanize,
name: scope ? "#{scope}[#{method}]" : method.to_s
}.merge(options)
end
def email_field(method, options = {})
@output[:elements][scope ? "#{scope}_#{method}".to_sym : method.to_s.to_sym] = {
type: "email",
label: method.to_s.humanize,
name: scope ? "#{scope}[#{method}]" : method.to_s
}.merge(options)
end
def to_h
@output
end
end
RSpec.describe "form props" do
it "builds default attributes for the form" do
props = form_props {}
expect(props).to eq(
accept_charset: "UTF-8",
action: "/",
method: "post",
elements: {}
)
end
it "sets attributes for the form" do
props = form_props(url: "/some_url", method: "get") {}
expect(props).to eq(
accept_charset: "UTF-8",
action: "/some_url",
method: "get",
elements: {}
)
end
it "builds attributes for the form elements" do
props = form_props do |my_form_builder|
my_form_builder.text_field :first_name, min: 50
my_form_builder.email_field :email, required: true, label: "Email address"
end
expect(props[:elements]).to eq(
first_name: {
type: "text",
min: 50,
label: "First name",
name: "first_name"
},
email: {
type: "email",
required: true,
label: "Email address",
name: "email"
}
)
end
it "scopes form elements" do
props = form_props(scope: "user") do |my_form_builder|
my_form_builder.text_field :first_name
my_form_builder.email_field :email
end
expect(props[:elements]).to eq(
user_first_name: {
type: "text",
label: "First name",
name: "user[first_name]"
},
user_email: {
type: "email",
label: "Email",
name: "user[email]"
}
)
end
end
Closing this as https://github.com/thoughtbot/form_props has been created! 🥳