From d37c99845c97df69db02872df73571561a6646a7 Mon Sep 17 00:00:00 2001 From: Andrew Waller <48367637+andrewhwaller@users.noreply.github.com> Date: Fri, 12 Sep 2025 22:27:32 -0500 Subject: [PATCH 1/3] Add Requirable concern --- .../rails/components/concerns/requirable.rb | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 lib/superform/rails/components/concerns/requirable.rb diff --git a/lib/superform/rails/components/concerns/requirable.rb b/lib/superform/rails/components/concerns/requirable.rb new file mode 100644 index 0000000..b8425eb --- /dev/null +++ b/lib/superform/rails/components/concerns/requirable.rb @@ -0,0 +1,24 @@ +module Superform + module Rails + module Components + module Concerns + module Requirable + def field_attributes + super.merge(validation_attributes) + end + + def validation_attributes + return {} unless presence_validated? + { required: true } + end + + def presence_validated? + object = field.parent&.object + return false unless object&.class&.respond_to?(:validators_on) + object.class.validators_on(field.key).any? { |v| v.kind == :presence } + end + end + end + end + end +end From 32d8365fd92b4f494729a23994c48f93177fdca3 Mon Sep 17 00:00:00 2001 From: Andrew Waller <48367637+andrewhwaller@users.noreply.github.com> Date: Fri, 12 Sep 2025 22:28:39 -0500 Subject: [PATCH 2/3] Add specs for client-side required validations via Requirable concern --- .../rails/field_convenience_methods_spec.rb | 76 ++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/spec/superform/rails/field_convenience_methods_spec.rb b/spec/superform/rails/field_convenience_methods_spec.rb index c3d9196..fff58a3 100644 --- a/spec/superform/rails/field_convenience_methods_spec.rb +++ b/spec/superform/rails/field_convenience_methods_spec.rb @@ -60,4 +60,78 @@ expect(component.type).to eq("radio") end end -end \ No newline at end of file + + describe "HTML5 client-side validations" do + context "input" do + it "adds required when presence validation exists" do + component = field.input + expect(component.field_attributes[:required]).to eq(true) + end + + it "does not add required when no presence validation" do + component = form.field(:last_name).input + expect(component.field_attributes.key?(:required)).to eq(false) + end + + it "allows required: false to override" do + component = field.input(required: false) + attrs = component.send(:attributes) + expect(attrs[:required]).to eq(false) + end + end + + context "checkbox" do + it "adds required when presence validation exists" do + component = field.checkbox + expect(component.field_attributes[:required]).to eq(true) + end + + it "does not add required when no presence validation" do + component = form.field(:last_name).checkbox + expect(component.field_attributes.key?(:required)).to eq(false) + end + + it "allows required: false to override" do + component = field.checkbox(required: false) + attrs = component.send(:attributes) + expect(attrs[:required]).to eq(false) + end + end + + context "textarea" do + it "adds required when presence validation exists" do + component = field.textarea + expect(component.field_attributes[:required]).to eq(true) + end + + it "does not add required when no presence validation" do + component = form.field(:last_name).textarea + expect(component.field_attributes.key?(:required)).to eq(false) + end + + it "allows required: false to override" do + component = field.textarea(required: false) + attrs = component.send(:attributes) + expect(attrs[:required]).to eq(false) + end + end + + context "select" do + it "adds required when presence validation exists" do + component = field.select("a", "b") + expect(component.field_attributes[:required]).to eq(true) + end + + it "does not add required when no presence validation" do + component = form.field(:last_name).select("a", "b") + expect(component.field_attributes.key?(:required)).to eq(false) + end + + it "allows required: false to override" do + component = field.select("a", "b", required: false) + attrs = component.send(:attributes) + expect(attrs[:required]).to eq(false) + end + end + end +end From 3c3e09cb8f0ed7df143ee373c164373a1db13f06 Mon Sep 17 00:00:00 2001 From: Andrew Waller <48367637+andrewhwaller@users.noreply.github.com> Date: Fri, 12 Sep 2025 22:29:03 -0500 Subject: [PATCH 3/3] Add Requirable concern to Checkbox, Input, Select, and Textarea --- lib/superform/rails/components/checkbox.rb | 4 +++- lib/superform/rails/components/input.rb | 4 +++- lib/superform/rails/components/select.rb | 4 +++- lib/superform/rails/components/textarea.rb | 4 +++- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/superform/rails/components/checkbox.rb b/lib/superform/rails/components/checkbox.rb index 0c40f73..09c3746 100644 --- a/lib/superform/rails/components/checkbox.rb +++ b/lib/superform/rails/components/checkbox.rb @@ -2,6 +2,8 @@ module Superform module Rails module Components class Checkbox < Field + prepend Concerns::Requirable + def view_template(&) # Rails has a hidden and checkbox input to deal with sending back a value # to the server regardless of if the input is checked or not. @@ -16,4 +18,4 @@ def field_attributes end end end -end \ No newline at end of file +end diff --git a/lib/superform/rails/components/input.rb b/lib/superform/rails/components/input.rb index 02e40b4..cee564d 100644 --- a/lib/superform/rails/components/input.rb +++ b/lib/superform/rails/components/input.rb @@ -2,6 +2,8 @@ module Superform module Rails module Components class Input < Field + prepend Concerns::Requirable + def view_template(&) input(**attributes) end @@ -56,4 +58,4 @@ def attribute_type end end end -end \ No newline at end of file +end diff --git a/lib/superform/rails/components/select.rb b/lib/superform/rails/components/select.rb index 8979591..5c86b8b 100644 --- a/lib/superform/rails/components/select.rb +++ b/lib/superform/rails/components/select.rb @@ -2,6 +2,8 @@ module Superform module Rails module Components class Select < Field + prepend Concerns::Requirable + def initialize(*, collection: [], **, &) super(*, **, &) @collection = collection @@ -40,4 +42,4 @@ def map_options(collection) end end end -end \ No newline at end of file +end diff --git a/lib/superform/rails/components/textarea.rb b/lib/superform/rails/components/textarea.rb index 27357db..2667a74 100644 --- a/lib/superform/rails/components/textarea.rb +++ b/lib/superform/rails/components/textarea.rb @@ -2,6 +2,8 @@ module Superform module Rails module Components class Textarea < Field + prepend Concerns::Requirable + def view_template(&content) content ||= Proc.new { dom.value } textarea(**attributes, &content) @@ -9,4 +11,4 @@ def view_template(&content) end end end -end \ No newline at end of file +end