diff --git a/dist/client-side-validations.esm.js b/dist/client-side-validations.esm.js
index 7cd7a60a5..0b42a8c5a 100644
--- a/dist/client-side-validations.esm.js
+++ b/dist/client-side-validations.esm.js
@@ -626,6 +626,8 @@ var cleanNestedElementName = function cleanNestedElementName(elementName, nested
var cleanElementName = function cleanElementName(elementName, validators) {
elementName = elementName.replace(/\[(\w+_attributes)\]\[[\da-z_]+\](?=\[(?:\w+_attributes)\])/g, '[$1][]');
+ elementName = elementName.replace(/\(\di\)/g, ''); // date/time_select (1/2/3/4/5i) fields
+
var nestedMatches = elementName.match(/\[(\w+_attributes)\].*\[(\w+)\]$/);
if (nestedMatches) {
diff --git a/dist/client-side-validations.js b/dist/client-side-validations.js
index 94f5658dd..f94a2fcea 100644
--- a/dist/client-side-validations.js
+++ b/dist/client-side-validations.js
@@ -632,6 +632,8 @@
var cleanElementName = function cleanElementName(elementName, validators) {
elementName = elementName.replace(/\[(\w+_attributes)\]\[[\da-z_]+\](?=\[(?:\w+_attributes)\])/g, '[$1][]');
+ elementName = elementName.replace(/\(\di\)/g, ''); // date/time_select (1/2/3/4/5i) fields
+
var nestedMatches = elementName.match(/\[(\w+_attributes)\].*\[(\w+)\]$/);
if (nestedMatches) {
diff --git a/lib/client_side_validations/action_view/form_builder.rb b/lib/client_side_validations/action_view/form_builder.rb
index 5baa9e700..121ea47ed 100644
--- a/lib/client_side_validations/action_view/form_builder.rb
+++ b/lib/client_side_validations/action_view/form_builder.rb
@@ -62,6 +62,15 @@ def collection_select(method, collection, value_method, text_method, options = {
super(method, collection, value_method, text_method, options, html_options)
end
+ %i[date_select datetime_select time_select].each do |method_name|
+ define_method method_name do |method, options = {}, html_options = {}|
+ build_validation_options(method, options)
+ html_options.delete(:validate)
+
+ super(method, options, html_options)
+ end
+ end
+
def fields_for(record_name, record_object = nil, fields_options = {}, &block)
if record_object.is_a?(Hash) && record_object.extractable_options?
fields_options = record_object
diff --git a/src/main.js b/src/main.js
index fe6ae0410..f2d45b98a 100644
--- a/src/main.js
+++ b/src/main.js
@@ -82,6 +82,7 @@ const cleanNestedElementName = (elementName, nestedMatches, validators) => {
const cleanElementName = (elementName, validators) => {
elementName = elementName.replace(/\[(\w+_attributes)\]\[[\da-z_]+\](?=\[(?:\w+_attributes)\])/g, '[$1][]')
+ elementName = elementName.replace(/\(\di\)/g, '') // date/time_select (1/2/3/4/5i) fields
const nestedMatches = elementName.match(/\[(\w+_attributes)\].*\[(\w+)\]$/)
diff --git a/test/action_view/cases/test_form_for_helpers.rb b/test/action_view/cases/test_form_for_helpers.rb
index c1460ba61..cfb109548 100644
--- a/test/action_view/cases/test_form_for_helpers.rb
+++ b/test/action_view/cases/test_form_for_helpers.rb
@@ -432,6 +432,42 @@ def test_time_zone_select_with_validate_options
assert_dom_equal expected, output_buffer
end
+ def test_date_select
+ form_for(@post, validate: true) do |f|
+ concat f.date_select(:cost)
+ end
+
+ validators = { 'post[cost]' => { presence: [{ message: "can't be blank" }] } }
+ expected = whole_form_for('/posts', 'new_post', 'new_post', validators: validators) do
+ date_select :post, :cost
+ end
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_datetime_select
+ form_for(@post, validate: true) do |f|
+ concat f.datetime_select(:cost)
+ end
+
+ validators = { 'post[cost]' => { presence: [{ message: "can't be blank" }] } }
+ expected = whole_form_for('/posts', 'new_post', 'new_post', validators: validators) do
+ datetime_select :post, :cost
+ end
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_time_select
+ form_for(@post, validate: true) do |f|
+ concat f.time_select(:cost)
+ end
+
+ validators = { 'post[cost]' => { presence: [{ message: "can't be blank" }] } }
+ expected = whole_form_for('/posts', 'new_post', 'new_post', validators: validators) do
+ time_select :post, :cost
+ end
+ assert_dom_equal expected, output_buffer
+ end
+
def test_as_form_option_with_new_record_rails
form_for(@post, as: :article, validate: true) do
concat content_tag(:span, 'Dummy Content')
diff --git a/test/action_view/cases/test_form_with_helpers.rb b/test/action_view/cases/test_form_with_helpers.rb
index 792f3accb..8377d417b 100644
--- a/test/action_view/cases/test_form_with_helpers.rb
+++ b/test/action_view/cases/test_form_with_helpers.rb
@@ -462,6 +462,43 @@ def test_form_with_time_zone_select_with_validate_options
assert_dom_equal expected, output_buffer
end
+ def test_form_with_date_select
+ form_with(model: @post, validate: true) do |f|
+ concat f.date_select(:cost)
+ end
+
+ validators = { 'post[cost]' => { presence: [{ message: "can't be blank" }] } }
+ expected = whole_form_with('/posts', validators: validators) do
+ date_select :post, :cost
+ end
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_form_with_datetime_select
+ form_with(model: @post, validate: true) do |f|
+ concat f.datetime_select(:cost)
+ end
+
+ validators = { 'post[cost]' => { presence: [{ message: "can't be blank" }] } }
+ expected = whole_form_with('/posts', validators: validators) do
+ datetime_select :post, :cost
+ end
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_form_with_time_select
+ form_with(model: @post, validate: true) do |f|
+ concat f.time_select(:cost)
+ end
+
+ validators = { 'post[cost]' => { presence: [{ message: "can't be blank" }] } }
+ expected = whole_form_with('/posts', validators: validators) do
+ time_select :post, :cost
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
def test_form_with_as_form_option_with_new_record_rails
form_with(model: @post, as: :article, validate: true) do
concat content_tag(:span, 'Dummy Content')
diff --git a/test/action_view/cases/test_legacy_form_for_helpers.rb b/test/action_view/cases/test_legacy_form_for_helpers.rb
index 1e5c0b0db..0a04ae18d 100644
--- a/test/action_view/cases/test_legacy_form_for_helpers.rb
+++ b/test/action_view/cases/test_legacy_form_for_helpers.rb
@@ -41,6 +41,39 @@ def test_file_field
assert_dom_equal expected, output_buffer
end
+ def test_date_select
+ form_for(@post) do |f|
+ concat f.date_select(:cost)
+ end
+
+ expected = whole_form_for('/posts', 'new_post', 'new_post') do
+ date_select :post, :cost
+ end
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_datetime_select
+ form_for(@post) do |f|
+ concat f.datetime_select(:cost)
+ end
+
+ expected = whole_form_for('/posts', 'new_post', 'new_post') do
+ datetime_select :post, :cost
+ end
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_time_select
+ form_for(@post) do |f|
+ concat f.time_select(:cost)
+ end
+
+ expected = whole_form_for('/posts', 'new_post', 'new_post') do
+ time_select :post, :cost
+ end
+ assert_dom_equal expected, output_buffer
+ end
+
def test_check_box
form_for(@post) do |f|
concat f.check_box(:cost)
diff --git a/test/action_view/cases/test_legacy_form_with_helpers.rb b/test/action_view/cases/test_legacy_form_with_helpers.rb
index 7cc3b08b3..c2964b3d5 100644
--- a/test/action_view/cases/test_legacy_form_with_helpers.rb
+++ b/test/action_view/cases/test_legacy_form_with_helpers.rb
@@ -46,6 +46,40 @@ def test_form_with_file_field
assert_dom_equal expected, output_buffer
end
+ def test_form_with_date_select
+ form_with(model: @post) do |f|
+ concat f.date_select(:cost)
+ end
+
+ expected = whole_form_with('/posts') do
+ date_select :post, :cost
+ end
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_form_with_datetime_select
+ form_with(model: @post) do |f|
+ concat f.datetime_select(:cost)
+ end
+
+ expected = whole_form_with('/posts') do
+ datetime_select :post, :cost
+ end
+ assert_dom_equal expected, output_buffer
+ end
+
+ def test_form_with_time_select
+ form_with(model: @post) do |f|
+ concat f.time_select(:cost)
+ end
+
+ expected = whole_form_with('/posts') do
+ time_select :post, :cost
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
+
def test_form_with_check_box
form_with(model: @post) do |f|
concat f.check_box(:cost)
diff --git a/test/javascript/public/test/validateElement.js b/test/javascript/public/test/validateElement.js
index 431e643f9..9809d9827 100644
--- a/test/javascript/public/test/validateElement.js
+++ b/test/javascript/public/test/validateElement.js
@@ -18,7 +18,10 @@ QUnit.module('Validate Element', {
'user[phone_numbers_attributes][deeply][nested][][attribute]': { presence: [{ message: 'must be present' }] },
'user[phone_numbers_attributes][][labels_attributes][][label]': { presence: [{ message: 'must be present' }] },
'user[a_attributes][][b_attributes][][c_attributes][][d_attributes][][e]': { presence: [{ message: 'must be present' }] },
- customized_field: { length: [{ messages: { minimum: 'is too short (minimum is 4 characters)' }, minimum: 4 }] }
+ customized_field: { length: [{ messages: { minimum: 'is too short (minimum is 4 characters)' }, minimum: 4 }] },
+ 'user[date_of_sign_up]': { presence: [{ message: 'must be present' }] },
+ 'user[date_of_birth]': { presence: [{ message: 'must be present' }] },
+ 'user[time_of_birth]': { presence: [{ message: 'must be present' }] },
}
}
@@ -132,7 +135,73 @@ QUnit.module('Validate Element', {
id: 'customized_field',
type: 'text'
}))
-
+ .append($(''))
+ .append($('', {
+ name: 'user[date_of_sign_up]',
+ id: 'user_date_of_sign_up',
+ type: 'date'
+ }))
+ .append($(''))
+ .append($('', {
+ type: 'hidden',
+ id: 'user_time_of_birth_1i',
+ name: 'user[time_of_birth(1i)]',
+ value: 1
+ }))
+ .append($('', {
+ type: 'hidden',
+ id: 'user_time_of_birth_2i',
+ name: 'user[time_of_birth(2i)]',
+ value: 1
+ }))
+ .append($('', {
+ type: 'hidden',
+ id: 'user_time_of_birth_3i',
+ name: 'user[time_of_birth(3i)]',
+ value: 1
+ }))
+ .append($('