From 2c6321ced8c6b693a78fe224a6da2d4aa4f1baae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Djalma=20Ara=C3=BAjo?= Date: Mon, 22 Jun 2026 17:07:53 -0300 Subject: [PATCH 1/2] [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 --- docs/app/views/docs/form.rb | 91 +++++++++++++++++++++++++++++++ gem/lib/ruby_ui/form/form_docs.rb | 91 +++++++++++++++++++++++++++++++ 2 files changed, 182 insertions(+) diff --git a/docs/app/views/docs/form.rb b/docs/app/views/docs/form.rb index 499ad4b65..4f5b0440c 100644 --- a/docs/app/views/docs/form.rb +++ b/docs/app/views/docs/form.rb @@ -170,6 +170,97 @@ def view_template RUBY end + Heading(level: 2) { "Rails Integration" } + + Text do + plain "RubyUI Form components are plain HTML — they work with any form submission strategy. " + plain "The recommended approach for Rails apps is to use " + InlineLink(href: "https://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-form_with", target: "_blank") { "form_with" } + plain " to generate the " + code(class: "font-mono text-sm") { "action" } + plain " URL and CSRF token, then pass explicit " + code(class: "font-mono text-sm") { "name" } + plain " / " + code(class: "font-mono text-sm") { "id" } + plain " attributes to each RubyUI input so the browser serialises them correctly. " + plain "Server-side errors can be surfaced by rendering " + code(class: "font-mono text-sm") { "FormFieldError" } + plain " with content from " + code(class: "font-mono text-sm") { "model.errors.full_messages_for(:attr)" } + plain "." + end + + render Docs::VisualCodeExample.new(title: "Minimal Rails form", context: self) do + <<~RUBY + # In your Phlex view, call form_with via helpers: + # form_with(url: users_path, method: :post) passes action + CSRF automatically. + # + # You can also set action and the CSRF token manually: + Form(action: helpers.users_path, method: "post", class: "w-2/3 space-y-6") do + input(type: "hidden", name: "authenticity_token", value: helpers.form_authenticity_token) + + FormField do + FormFieldLabel(for: "user_email") { "Email" } + Input( + type: "email", + id: "user_email", + name: "user[email]", + placeholder: "you@example.com", + required: true + ) + FormFieldError() + end + + Button(type: "submit") { "Continue" } + end + RUBY + end + + render Docs::VisualCodeExample.new(title: "Devise-style login form", context: self) do + <<~RUBY + # Full sign-in form mirroring Devise session[email] / session[password] params. + # Pass backend errors (e.g. "Invalid email or password") into FormFieldError. + Form(action: helpers.user_session_path, method: "post", class: "space-y-6") do + input(type: "hidden", name: "authenticity_token", value: helpers.form_authenticity_token) + + FormField do + FormFieldLabel(for: "session_email") { "Email" } + Input( + type: "email", + id: "session_email", + name: "session[email]", + placeholder: "you@example.com", + autocomplete: "email", + required: true + ) + FormFieldError { @error_message } + end + + FormField do + FormFieldLabel(for: "session_password") { "Password" } + Input( + type: "password", + id: "session_password", + name: "session[password]", + autocomplete: "current-password", + required: true, + minlength: "8" + ) + FormFieldError() + end + + FormField do + div(class: "flex items-center gap-2") do + Checkbox(id: "session_remember_me", name: "session[remember_me]", value: "1") + FormFieldLabel(for: "session_remember_me") { "Remember me" } + end + end + + Button(type: "submit", class: "w-full") { "Sign in" } + end + RUBY + end + render Components::ComponentSetup::Tabs.new(component_name: component) render Docs::ComponentsTable.new(component_files(component)) diff --git a/gem/lib/ruby_ui/form/form_docs.rb b/gem/lib/ruby_ui/form/form_docs.rb index 499ad4b65..4f5b0440c 100644 --- a/gem/lib/ruby_ui/form/form_docs.rb +++ b/gem/lib/ruby_ui/form/form_docs.rb @@ -170,6 +170,97 @@ def view_template RUBY end + Heading(level: 2) { "Rails Integration" } + + Text do + plain "RubyUI Form components are plain HTML — they work with any form submission strategy. " + plain "The recommended approach for Rails apps is to use " + InlineLink(href: "https://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-form_with", target: "_blank") { "form_with" } + plain " to generate the " + code(class: "font-mono text-sm") { "action" } + plain " URL and CSRF token, then pass explicit " + code(class: "font-mono text-sm") { "name" } + plain " / " + code(class: "font-mono text-sm") { "id" } + plain " attributes to each RubyUI input so the browser serialises them correctly. " + plain "Server-side errors can be surfaced by rendering " + code(class: "font-mono text-sm") { "FormFieldError" } + plain " with content from " + code(class: "font-mono text-sm") { "model.errors.full_messages_for(:attr)" } + plain "." + end + + render Docs::VisualCodeExample.new(title: "Minimal Rails form", context: self) do + <<~RUBY + # In your Phlex view, call form_with via helpers: + # form_with(url: users_path, method: :post) passes action + CSRF automatically. + # + # You can also set action and the CSRF token manually: + Form(action: helpers.users_path, method: "post", class: "w-2/3 space-y-6") do + input(type: "hidden", name: "authenticity_token", value: helpers.form_authenticity_token) + + FormField do + FormFieldLabel(for: "user_email") { "Email" } + Input( + type: "email", + id: "user_email", + name: "user[email]", + placeholder: "you@example.com", + required: true + ) + FormFieldError() + end + + Button(type: "submit") { "Continue" } + end + RUBY + end + + render Docs::VisualCodeExample.new(title: "Devise-style login form", context: self) do + <<~RUBY + # Full sign-in form mirroring Devise session[email] / session[password] params. + # Pass backend errors (e.g. "Invalid email or password") into FormFieldError. + Form(action: helpers.user_session_path, method: "post", class: "space-y-6") do + input(type: "hidden", name: "authenticity_token", value: helpers.form_authenticity_token) + + FormField do + FormFieldLabel(for: "session_email") { "Email" } + Input( + type: "email", + id: "session_email", + name: "session[email]", + placeholder: "you@example.com", + autocomplete: "email", + required: true + ) + FormFieldError { @error_message } + end + + FormField do + FormFieldLabel(for: "session_password") { "Password" } + Input( + type: "password", + id: "session_password", + name: "session[password]", + autocomplete: "current-password", + required: true, + minlength: "8" + ) + FormFieldError() + end + + FormField do + div(class: "flex items-center gap-2") do + Checkbox(id: "session_remember_me", name: "session[remember_me]", value: "1") + FormFieldLabel(for: "session_remember_me") { "Remember me" } + end + end + + Button(type: "submit", class: "w-full") { "Sign in" } + end + RUBY + end + render Components::ComponentSetup::Tabs.new(component_name: component) render Docs::ComponentsTable.new(component_files(component)) From 55fd108b7d9fb2b795cf433b7f18a89cedda36ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Djalma=20Ara=C3=BAjo?= Date: Mon, 22 Jun 2026 17:44:55 -0300 Subject: [PATCH 2/2] [Documentation] Form: make Rails examples code-only and rebuild MCP registry (#434) Convert the two Rails-integration examples ("Minimal Rails form" and "Devise-style login form") from VisualCodeExample to Heading + Codeblock so they are displayed as syntax-highlighted code without being live-rendered via instance_eval. This fixes the ComponentsControllerTest crash caused by helpers.users_path and helpers.user_session_path not existing in the docs app. Also rebuild mcp/data/registry.json to include the updated form docs. --- docs/app/views/docs/form.rb | 122 +++++++++++++++--------------- gem/lib/ruby_ui/form/form_docs.rb | 122 +++++++++++++++--------------- mcp/data/registry.json | 2 +- 3 files changed, 121 insertions(+), 125 deletions(-) diff --git a/docs/app/views/docs/form.rb b/docs/app/views/docs/form.rb index 4f5b0440c..2340dad1f 100644 --- a/docs/app/views/docs/form.rb +++ b/docs/app/views/docs/form.rb @@ -190,76 +190,74 @@ def view_template plain "." end - render Docs::VisualCodeExample.new(title: "Minimal Rails form", context: self) do - <<~RUBY - # In your Phlex view, call form_with via helpers: - # form_with(url: users_path, method: :post) passes action + CSRF automatically. - # - # You can also set action and the CSRF token manually: - Form(action: helpers.users_path, method: "post", class: "w-2/3 space-y-6") do - input(type: "hidden", name: "authenticity_token", value: helpers.form_authenticity_token) + Heading(level: 3) { "Minimal Rails form" } + Codeblock(<<~RUBY, syntax: :ruby) + # In your Phlex view, call form_with via helpers: + # form_with(url: users_path, method: :post) passes action + CSRF automatically. + # + # You can also set action and the CSRF token manually: + Form(action: helpers.users_path, method: "post", class: "w-2/3 space-y-6") do + input(type: "hidden", name: "authenticity_token", value: helpers.form_authenticity_token) - FormField do - FormFieldLabel(for: "user_email") { "Email" } - Input( - type: "email", - id: "user_email", - name: "user[email]", - placeholder: "you@example.com", - required: true - ) - FormFieldError() - end - - Button(type: "submit") { "Continue" } + FormField do + FormFieldLabel(for: "user_email") { "Email" } + Input( + type: "email", + id: "user_email", + name: "user[email]", + placeholder: "you@example.com", + required: true + ) + FormFieldError() end - RUBY - end - render Docs::VisualCodeExample.new(title: "Devise-style login form", context: self) do - <<~RUBY - # Full sign-in form mirroring Devise session[email] / session[password] params. - # Pass backend errors (e.g. "Invalid email or password") into FormFieldError. - Form(action: helpers.user_session_path, method: "post", class: "space-y-6") do - input(type: "hidden", name: "authenticity_token", value: helpers.form_authenticity_token) + Button(type: "submit") { "Continue" } + end + RUBY - FormField do - FormFieldLabel(for: "session_email") { "Email" } - Input( - type: "email", - id: "session_email", - name: "session[email]", - placeholder: "you@example.com", - autocomplete: "email", - required: true - ) - FormFieldError { @error_message } - end + Heading(level: 3) { "Devise-style login form" } + Codeblock(<<~RUBY, syntax: :ruby) + # Full sign-in form mirroring Devise session[email] / session[password] params. + # Pass backend errors (e.g. "Invalid email or password") into FormFieldError. + Form(action: helpers.user_session_path, method: "post", class: "space-y-6") do + input(type: "hidden", name: "authenticity_token", value: helpers.form_authenticity_token) - FormField do - FormFieldLabel(for: "session_password") { "Password" } - Input( - type: "password", - id: "session_password", - name: "session[password]", - autocomplete: "current-password", - required: true, - minlength: "8" - ) - FormFieldError() - end + FormField do + FormFieldLabel(for: "session_email") { "Email" } + Input( + type: "email", + id: "session_email", + name: "session[email]", + placeholder: "you@example.com", + autocomplete: "email", + required: true + ) + FormFieldError { @error_message } + end - FormField do - div(class: "flex items-center gap-2") do - Checkbox(id: "session_remember_me", name: "session[remember_me]", value: "1") - FormFieldLabel(for: "session_remember_me") { "Remember me" } - end - end + FormField do + FormFieldLabel(for: "session_password") { "Password" } + Input( + type: "password", + id: "session_password", + name: "session[password]", + autocomplete: "current-password", + required: true, + minlength: "8" + ) + FormFieldError() + end - Button(type: "submit", class: "w-full") { "Sign in" } + FormField do + div(class: "flex items-center gap-2") do + Checkbox(id: "session_remember_me", name: "session[remember_me]", value: "1") + FormFieldLabel(for: "session_remember_me") { "Remember me" } + end end - RUBY - end + + Button(type: "submit", class: "w-full") { "Sign in" } + end + RUBY render Components::ComponentSetup::Tabs.new(component_name: component) diff --git a/gem/lib/ruby_ui/form/form_docs.rb b/gem/lib/ruby_ui/form/form_docs.rb index 4f5b0440c..2340dad1f 100644 --- a/gem/lib/ruby_ui/form/form_docs.rb +++ b/gem/lib/ruby_ui/form/form_docs.rb @@ -190,76 +190,74 @@ def view_template plain "." end - render Docs::VisualCodeExample.new(title: "Minimal Rails form", context: self) do - <<~RUBY - # In your Phlex view, call form_with via helpers: - # form_with(url: users_path, method: :post) passes action + CSRF automatically. - # - # You can also set action and the CSRF token manually: - Form(action: helpers.users_path, method: "post", class: "w-2/3 space-y-6") do - input(type: "hidden", name: "authenticity_token", value: helpers.form_authenticity_token) + Heading(level: 3) { "Minimal Rails form" } + Codeblock(<<~RUBY, syntax: :ruby) + # In your Phlex view, call form_with via helpers: + # form_with(url: users_path, method: :post) passes action + CSRF automatically. + # + # You can also set action and the CSRF token manually: + Form(action: helpers.users_path, method: "post", class: "w-2/3 space-y-6") do + input(type: "hidden", name: "authenticity_token", value: helpers.form_authenticity_token) - FormField do - FormFieldLabel(for: "user_email") { "Email" } - Input( - type: "email", - id: "user_email", - name: "user[email]", - placeholder: "you@example.com", - required: true - ) - FormFieldError() - end - - Button(type: "submit") { "Continue" } + FormField do + FormFieldLabel(for: "user_email") { "Email" } + Input( + type: "email", + id: "user_email", + name: "user[email]", + placeholder: "you@example.com", + required: true + ) + FormFieldError() end - RUBY - end - render Docs::VisualCodeExample.new(title: "Devise-style login form", context: self) do - <<~RUBY - # Full sign-in form mirroring Devise session[email] / session[password] params. - # Pass backend errors (e.g. "Invalid email or password") into FormFieldError. - Form(action: helpers.user_session_path, method: "post", class: "space-y-6") do - input(type: "hidden", name: "authenticity_token", value: helpers.form_authenticity_token) + Button(type: "submit") { "Continue" } + end + RUBY - FormField do - FormFieldLabel(for: "session_email") { "Email" } - Input( - type: "email", - id: "session_email", - name: "session[email]", - placeholder: "you@example.com", - autocomplete: "email", - required: true - ) - FormFieldError { @error_message } - end + Heading(level: 3) { "Devise-style login form" } + Codeblock(<<~RUBY, syntax: :ruby) + # Full sign-in form mirroring Devise session[email] / session[password] params. + # Pass backend errors (e.g. "Invalid email or password") into FormFieldError. + Form(action: helpers.user_session_path, method: "post", class: "space-y-6") do + input(type: "hidden", name: "authenticity_token", value: helpers.form_authenticity_token) - FormField do - FormFieldLabel(for: "session_password") { "Password" } - Input( - type: "password", - id: "session_password", - name: "session[password]", - autocomplete: "current-password", - required: true, - minlength: "8" - ) - FormFieldError() - end + FormField do + FormFieldLabel(for: "session_email") { "Email" } + Input( + type: "email", + id: "session_email", + name: "session[email]", + placeholder: "you@example.com", + autocomplete: "email", + required: true + ) + FormFieldError { @error_message } + end - FormField do - div(class: "flex items-center gap-2") do - Checkbox(id: "session_remember_me", name: "session[remember_me]", value: "1") - FormFieldLabel(for: "session_remember_me") { "Remember me" } - end - end + FormField do + FormFieldLabel(for: "session_password") { "Password" } + Input( + type: "password", + id: "session_password", + name: "session[password]", + autocomplete: "current-password", + required: true, + minlength: "8" + ) + FormFieldError() + end - Button(type: "submit", class: "w-full") { "Sign in" } + FormField do + div(class: "flex items-center gap-2") do + Checkbox(id: "session_remember_me", name: "session[remember_me]", value: "1") + FormFieldLabel(for: "session_remember_me") { "Remember me" } + end end - RUBY - end + + Button(type: "submit", class: "w-full") { "Sign in" } + end + RUBY render Components::ComponentSetup::Tabs.new(component_name: component) diff --git a/mcp/data/registry.json b/mcp/data/registry.json index 8fa00b3f8..908f89c1c 100644 --- a/mcp/data/registry.json +++ b/mcp/data/registry.json @@ -1394,7 +1394,7 @@ "gems": [] }, "install_command": "rails g ruby_ui:component Form", - "docs_markdown": "# Form\n\nBuilding forms with built-in client-side validations.\n\n## Usage\n\n### Example\n\n```ruby\nForm(class: \"w-2/3 space-y-6\") do\n FormField do\n FormFieldLabel { \"Default error\" }\n Input(placeholder: \"Joel Drapper\", required: true, minlength: \"3\") { \"Joel Drapper\" }\n FormFieldHint()\n FormFieldError()\n end\n Button(type: \"submit\") { \"Save\" }\nend\n```\n\n### Disabled\n\n```ruby\nFormField do\n FormFieldLabel { \"Disabled\" }\n Input(disabled: true, placeholder: \"Joel Drapper\", required: true, minlength: \"3\") { \"Joel Drapper\" }\nend\n```\n\n### Aria Disabled\n\n```ruby\nFormField do\n FormFieldLabel { \"Aria Disabled\" }\n Input(aria: {disabled: \"true\"}, placeholder: \"Joel Drapper\", required: true, minlength: \"3\") { \"Joel Drapper\" }\nend\n```\n\n### Custom error message\n\n```ruby\nForm(class: \"w-2/3 space-y-6\") do\n FormField do\n FormFieldLabel { \"Custom error message\" }\n Input(placeholder: \"joel@drapper.me\", required: true, data_value_missing: \"Custom error message\")\n FormFieldError()\n end\n Button(type: \"submit\") { \"Save\" }\nend\n```\n\n### Backend error\n\n```ruby\nForm(class: \"w-2/3 space-y-6\") do\n FormField do\n FormFieldLabel { \"Backend error\" }\n Input(placeholder: \"Joel Drapper\", required: true)\n FormFieldError { \"Error from backend\" }\n end\n Button(type: \"submit\") { \"Save\" }\nend\n```\n\n### Checkbox\n\n```ruby\nForm(class: \"w-2/3 space-y-6\") do\n FormField do\n Checkbox(required: true)\n label(\n class:\n \"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70\"\n ) { \" Accept terms and conditions \" }\n FormFieldError()\n end\n Button(type: \"submit\") { \"Save\" }\nend\n```\n\n### Select\n\n```ruby\nForm(class: \"w-2/3 space-y-6\") do\n FormField do\n FormFieldLabel { \"Select\" }\n Select do\n SelectInput(required: true)\n SelectTrigger do\n SelectValue(placeholder: \"Select a fruit\")\n end\n SelectContent() do\n SelectGroup do\n SelectLabel { \"Fruits\" }\n SelectItem(value: \"apple\") { \"Apple\" }\n SelectItem(value: \"orange\") { \"Orange\" }\n SelectItem(value: \"banana\") { \"Banana\" }\n SelectItem(value: \"watermelon\") { \"Watermelon\" }\n end\n end\n end\n FormFieldError()\n end\n Button(type: \"submit\") { \"Save\" }\nend\n```\n\n### Combobox\n\n```ruby\nForm(class: \"w-2/3 space-y-6\") do\n FormField do\n FormFieldLabel { \"Combobox\" }\n\n Combobox do\n ComboboxTrigger placeholder: \"Pick value\"\n\n ComboboxPopover do\n ComboboxSearchInput(placeholder: \"Pick value or type anything\")\n\n ComboboxList do\n ComboboxEmptyState { \"No result\" }\n\n ComboboxListGroup label: \"Fruits\" do\n ComboboxItem do\n ComboboxRadio(name: \"food\", value: \"apple\", required: true)\n span { \"Apple\" }\n end\n\n ComboboxItem do\n ComboboxRadio(name: \"food\", value: \"banana\", required: true)\n span { \"Banana\" }\n end\n end\n\n ComboboxListGroup label: \"Vegetable\" do\n ComboboxItem do\n ComboboxRadio(name: \"food\", value: \"brocoli\", required: true)\n span { \"Broccoli\" }\n end\n\n ComboboxItem do\n ComboboxRadio(name: \"food\", value: \"carrot\", required: true)\n span { \"Carrot\" }\n end\n end\n\n ComboboxListGroup label: \"Others\" do\n ComboboxItem do\n ComboboxRadio(name: \"food\", value: \"chocolate\", required: true)\n span { \"Chocolate\" }\n end\n\n ComboboxItem do\n ComboboxRadio(name: \"food\", value: \"milk\", required: true)\n span { \"Milk\" }\n end\n end\n end\n end\n end\n\n FormFieldError()\n end\n Button(type: \"submit\") { \"Save\" }\nend\n```", + "docs_markdown": "# Form\n\nBuilding forms with built-in client-side validations.\n\n## Usage\n\n### Example\n\n```ruby\nForm(class: \"w-2/3 space-y-6\") do\n FormField do\n FormFieldLabel { \"Default error\" }\n Input(placeholder: \"Joel Drapper\", required: true, minlength: \"3\") { \"Joel Drapper\" }\n FormFieldHint()\n FormFieldError()\n end\n Button(type: \"submit\") { \"Save\" }\nend\n```\n\n### Disabled\n\n```ruby\nFormField do\n FormFieldLabel { \"Disabled\" }\n Input(disabled: true, placeholder: \"Joel Drapper\", required: true, minlength: \"3\") { \"Joel Drapper\" }\nend\n```\n\n### Aria Disabled\n\n```ruby\nFormField do\n FormFieldLabel { \"Aria Disabled\" }\n Input(aria: {disabled: \"true\"}, placeholder: \"Joel Drapper\", required: true, minlength: \"3\") { \"Joel Drapper\" }\nend\n```\n\n### Custom error message\n\n```ruby\nForm(class: \"w-2/3 space-y-6\") do\n FormField do\n FormFieldLabel { \"Custom error message\" }\n Input(placeholder: \"joel@drapper.me\", required: true, data_value_missing: \"Custom error message\")\n FormFieldError()\n end\n Button(type: \"submit\") { \"Save\" }\nend\n```\n\n### Backend error\n\n```ruby\nForm(class: \"w-2/3 space-y-6\") do\n FormField do\n FormFieldLabel { \"Backend error\" }\n Input(placeholder: \"Joel Drapper\", required: true)\n FormFieldError { \"Error from backend\" }\n end\n Button(type: \"submit\") { \"Save\" }\nend\n```\n\n### Checkbox\n\n```ruby\nForm(class: \"w-2/3 space-y-6\") do\n FormField do\n Checkbox(required: true)\n label(\n class:\n \"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70\"\n ) { \" Accept terms and conditions \" }\n FormFieldError()\n end\n Button(type: \"submit\") { \"Save\" }\nend\n```\n\n### Select\n\n```ruby\nForm(class: \"w-2/3 space-y-6\") do\n FormField do\n FormFieldLabel { \"Select\" }\n Select do\n SelectInput(required: true)\n SelectTrigger do\n SelectValue(placeholder: \"Select a fruit\")\n end\n SelectContent() do\n SelectGroup do\n SelectLabel { \"Fruits\" }\n SelectItem(value: \"apple\") { \"Apple\" }\n SelectItem(value: \"orange\") { \"Orange\" }\n SelectItem(value: \"banana\") { \"Banana\" }\n SelectItem(value: \"watermelon\") { \"Watermelon\" }\n end\n end\n end\n FormFieldError()\n end\n Button(type: \"submit\") { \"Save\" }\nend\n```\n\n### Combobox\n\n```ruby\nForm(class: \"w-2/3 space-y-6\") do\n FormField do\n FormFieldLabel { \"Combobox\" }\n\n Combobox do\n ComboboxTrigger placeholder: \"Pick value\"\n\n ComboboxPopover do\n ComboboxSearchInput(placeholder: \"Pick value or type anything\")\n\n ComboboxList do\n ComboboxEmptyState { \"No result\" }\n\n ComboboxListGroup label: \"Fruits\" do\n ComboboxItem do\n ComboboxRadio(name: \"food\", value: \"apple\", required: true)\n span { \"Apple\" }\n end\n\n ComboboxItem do\n ComboboxRadio(name: \"food\", value: \"banana\", required: true)\n span { \"Banana\" }\n end\n end\n\n ComboboxListGroup label: \"Vegetable\" do\n ComboboxItem do\n ComboboxRadio(name: \"food\", value: \"brocoli\", required: true)\n span { \"Broccoli\" }\n end\n\n ComboboxItem do\n ComboboxRadio(name: \"food\", value: \"carrot\", required: true)\n span { \"Carrot\" }\n end\n end\n\n ComboboxListGroup label: \"Others\" do\n ComboboxItem do\n ComboboxRadio(name: \"food\", value: \"chocolate\", required: true)\n span { \"Chocolate\" }\n end\n\n ComboboxItem do\n ComboboxRadio(name: \"food\", value: \"milk\", required: true)\n span { \"Milk\" }\n end\n end\n end\n end\n end\n\n FormFieldError()\n end\n Button(type: \"submit\") { \"Save\" }\nend\n```\n\n## Rails Integration\n\n### Minimal Rails form\n\n### Devise-style login form", "examples": [ { "title": "Example",