Skip to content

Commit 2c6321c

Browse files
committed
[Documentation] Form: document Rails form_with integration and real-world examples (#134)
Add a "Rails Integration" section to the Form docs covering: - Prose intro explaining the recommended strategy (form_with, explicit name/id, CSRF, FormFieldError for server errors) - Minimal single-field Rails form example with action, CSRF token, and FormFieldError - Devise-style login form example (email, password, remember me) with proper session[...] params and error handling
1 parent 1fc5e6c commit 2c6321c

2 files changed

Lines changed: 182 additions & 0 deletions

File tree

docs/app/views/docs/form.rb

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,97 @@ def view_template
170170
RUBY
171171
end
172172

173+
Heading(level: 2) { "Rails Integration" }
174+
175+
Text do
176+
plain "RubyUI Form components are plain HTML — they work with any form submission strategy. "
177+
plain "The recommended approach for Rails apps is to use "
178+
InlineLink(href: "https://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-form_with", target: "_blank") { "form_with" }
179+
plain " to generate the "
180+
code(class: "font-mono text-sm") { "action" }
181+
plain " URL and CSRF token, then pass explicit "
182+
code(class: "font-mono text-sm") { "name" }
183+
plain " / "
184+
code(class: "font-mono text-sm") { "id" }
185+
plain " attributes to each RubyUI input so the browser serialises them correctly. "
186+
plain "Server-side errors can be surfaced by rendering "
187+
code(class: "font-mono text-sm") { "FormFieldError" }
188+
plain " with content from "
189+
code(class: "font-mono text-sm") { "model.errors.full_messages_for(:attr)" }
190+
plain "."
191+
end
192+
193+
render Docs::VisualCodeExample.new(title: "Minimal Rails form", context: self) do
194+
<<~RUBY
195+
# In your Phlex view, call form_with via helpers:
196+
# form_with(url: users_path, method: :post) passes action + CSRF automatically.
197+
#
198+
# You can also set action and the CSRF token manually:
199+
Form(action: helpers.users_path, method: "post", class: "w-2/3 space-y-6") do
200+
input(type: "hidden", name: "authenticity_token", value: helpers.form_authenticity_token)
201+
202+
FormField do
203+
FormFieldLabel(for: "user_email") { "Email" }
204+
Input(
205+
type: "email",
206+
id: "user_email",
207+
name: "user[email]",
208+
placeholder: "you@example.com",
209+
required: true
210+
)
211+
FormFieldError()
212+
end
213+
214+
Button(type: "submit") { "Continue" }
215+
end
216+
RUBY
217+
end
218+
219+
render Docs::VisualCodeExample.new(title: "Devise-style login form", context: self) do
220+
<<~RUBY
221+
# Full sign-in form mirroring Devise session[email] / session[password] params.
222+
# Pass backend errors (e.g. "Invalid email or password") into FormFieldError.
223+
Form(action: helpers.user_session_path, method: "post", class: "space-y-6") do
224+
input(type: "hidden", name: "authenticity_token", value: helpers.form_authenticity_token)
225+
226+
FormField do
227+
FormFieldLabel(for: "session_email") { "Email" }
228+
Input(
229+
type: "email",
230+
id: "session_email",
231+
name: "session[email]",
232+
placeholder: "you@example.com",
233+
autocomplete: "email",
234+
required: true
235+
)
236+
FormFieldError { @error_message }
237+
end
238+
239+
FormField do
240+
FormFieldLabel(for: "session_password") { "Password" }
241+
Input(
242+
type: "password",
243+
id: "session_password",
244+
name: "session[password]",
245+
autocomplete: "current-password",
246+
required: true,
247+
minlength: "8"
248+
)
249+
FormFieldError()
250+
end
251+
252+
FormField do
253+
div(class: "flex items-center gap-2") do
254+
Checkbox(id: "session_remember_me", name: "session[remember_me]", value: "1")
255+
FormFieldLabel(for: "session_remember_me") { "Remember me" }
256+
end
257+
end
258+
259+
Button(type: "submit", class: "w-full") { "Sign in" }
260+
end
261+
RUBY
262+
end
263+
173264
render Components::ComponentSetup::Tabs.new(component_name: component)
174265

175266
render Docs::ComponentsTable.new(component_files(component))

gem/lib/ruby_ui/form/form_docs.rb

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,97 @@ def view_template
170170
RUBY
171171
end
172172

173+
Heading(level: 2) { "Rails Integration" }
174+
175+
Text do
176+
plain "RubyUI Form components are plain HTML — they work with any form submission strategy. "
177+
plain "The recommended approach for Rails apps is to use "
178+
InlineLink(href: "https://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-form_with", target: "_blank") { "form_with" }
179+
plain " to generate the "
180+
code(class: "font-mono text-sm") { "action" }
181+
plain " URL and CSRF token, then pass explicit "
182+
code(class: "font-mono text-sm") { "name" }
183+
plain " / "
184+
code(class: "font-mono text-sm") { "id" }
185+
plain " attributes to each RubyUI input so the browser serialises them correctly. "
186+
plain "Server-side errors can be surfaced by rendering "
187+
code(class: "font-mono text-sm") { "FormFieldError" }
188+
plain " with content from "
189+
code(class: "font-mono text-sm") { "model.errors.full_messages_for(:attr)" }
190+
plain "."
191+
end
192+
193+
render Docs::VisualCodeExample.new(title: "Minimal Rails form", context: self) do
194+
<<~RUBY
195+
# In your Phlex view, call form_with via helpers:
196+
# form_with(url: users_path, method: :post) passes action + CSRF automatically.
197+
#
198+
# You can also set action and the CSRF token manually:
199+
Form(action: helpers.users_path, method: "post", class: "w-2/3 space-y-6") do
200+
input(type: "hidden", name: "authenticity_token", value: helpers.form_authenticity_token)
201+
202+
FormField do
203+
FormFieldLabel(for: "user_email") { "Email" }
204+
Input(
205+
type: "email",
206+
id: "user_email",
207+
name: "user[email]",
208+
placeholder: "you@example.com",
209+
required: true
210+
)
211+
FormFieldError()
212+
end
213+
214+
Button(type: "submit") { "Continue" }
215+
end
216+
RUBY
217+
end
218+
219+
render Docs::VisualCodeExample.new(title: "Devise-style login form", context: self) do
220+
<<~RUBY
221+
# Full sign-in form mirroring Devise session[email] / session[password] params.
222+
# Pass backend errors (e.g. "Invalid email or password") into FormFieldError.
223+
Form(action: helpers.user_session_path, method: "post", class: "space-y-6") do
224+
input(type: "hidden", name: "authenticity_token", value: helpers.form_authenticity_token)
225+
226+
FormField do
227+
FormFieldLabel(for: "session_email") { "Email" }
228+
Input(
229+
type: "email",
230+
id: "session_email",
231+
name: "session[email]",
232+
placeholder: "you@example.com",
233+
autocomplete: "email",
234+
required: true
235+
)
236+
FormFieldError { @error_message }
237+
end
238+
239+
FormField do
240+
FormFieldLabel(for: "session_password") { "Password" }
241+
Input(
242+
type: "password",
243+
id: "session_password",
244+
name: "session[password]",
245+
autocomplete: "current-password",
246+
required: true,
247+
minlength: "8"
248+
)
249+
FormFieldError()
250+
end
251+
252+
FormField do
253+
div(class: "flex items-center gap-2") do
254+
Checkbox(id: "session_remember_me", name: "session[remember_me]", value: "1")
255+
FormFieldLabel(for: "session_remember_me") { "Remember me" }
256+
end
257+
end
258+
259+
Button(type: "submit", class: "w-full") { "Sign in" }
260+
end
261+
RUBY
262+
end
263+
173264
render Components::ComponentSetup::Tabs.new(component_name: component)
174265

175266
render Docs::ComponentsTable.new(component_files(component))

0 commit comments

Comments
 (0)