Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented date time fields #82

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@

source 'https://rubygems.org'

gem 'client_side_validations', github: 'MichalRemis/client_side_validations', branch: 'SupportDateTimeSelects'

gemspec
43 changes: 40 additions & 3 deletions dist/simple-form.bootstrap4.esm.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,19 @@
import $ from 'jquery';
import ClientSideValidations from '@client-side-validations/client-side-validations';

ClientSideValidations.formBuilders['SimpleForm::FormBuilder'] = {
var simpleFormFormBuilder = {
add: function add(element, settings, message) {
this.wrapper(settings.wrapper).add.call(this, element, settings, message);
this.wrapper(this.wrapperName(element, settings)).add.call(this, element, settings, message);
},
remove: function remove(element, settings) {
this.wrapper(settings.wrapper).remove.call(this, element, settings);
this.wrapper(this.wrapperName(element, settings)).remove.call(this, element, settings);
},
wrapper: function wrapper(name) {
return this.wrappers[name] || this.wrappers["default"];
},
wrapperName: function wrapperName(element, settings) {
return element.data('clientSideValidationsWrapper') || settings.wrapper;
},
wrappers: {
"default": {
add: function add(element, settings, message) {
Expand All @@ -42,6 +45,40 @@ ClientSideValidations.formBuilders['SimpleForm::FormBuilder'] = {
element.removeClass('is-invalid');
errorElement.remove();
}
},
multi_select: {
add: function add(element, settings, message) {
var wrapperElement = element.closest(settings.wrapper_tag + '.' + settings.wrapper_class.replace(/ /g, '.'));
var parentElement = element.parent();
var errorElement = wrapperElement.find(settings.error_tag + '.invalid-feedback');

if (!errorElement.length) {
errorElement = $('<' + settings.error_tag + '>', {
"class": 'invalid-feedback d-block',
text: message
});
parentElement.after(errorElement);
}

wrapperElement.addClass(settings.wrapper_error_class);
element.addClass('is-invalid');
errorElement.text(message);
},
remove: function remove(element, settings) {
var wrapperElement = element.closest(settings.wrapper_tag + '.' + settings.wrapper_class.replace(/ /g, '.'));
var errorElement = wrapperElement.find(settings.error_tag + '.invalid-feedback');
var invalidSiblingExists = element.siblings('.is-invalid').length;

if (!invalidSiblingExists) {
wrapperElement.removeClass(settings.wrapper_error_class);
errorElement.remove();
}

element.removeClass('is-invalid');
}
}
}
};
simpleFormFormBuilder.wrappers.horizontal_multi_select = simpleFormFormBuilder.wrappers.multi_select;
simpleFormFormBuilder.wrappers.vertical_multi_select = simpleFormFormBuilder.wrappers.multi_select;
ClientSideValidations.formBuilders['SimpleForm::FormBuilder'] = simpleFormFormBuilder;
43 changes: 40 additions & 3 deletions dist/simple-form.bootstrap4.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,19 @@
$ = $ && Object.prototype.hasOwnProperty.call($, 'default') ? $['default'] : $;
ClientSideValidations = ClientSideValidations && Object.prototype.hasOwnProperty.call(ClientSideValidations, 'default') ? ClientSideValidations['default'] : ClientSideValidations;

ClientSideValidations.formBuilders['SimpleForm::FormBuilder'] = {
var simpleFormFormBuilder = {
add: function add(element, settings, message) {
this.wrapper(settings.wrapper).add.call(this, element, settings, message);
this.wrapper(this.wrapperName(element, settings)).add.call(this, element, settings, message);
},
remove: function remove(element, settings) {
this.wrapper(settings.wrapper).remove.call(this, element, settings);
this.wrapper(this.wrapperName(element, settings)).remove.call(this, element, settings);
},
wrapper: function wrapper(name) {
return this.wrappers[name] || this.wrappers["default"];
},
wrapperName: function wrapperName(element, settings) {
return element.data('clientSideValidationsWrapper') || settings.wrapper;
},
wrappers: {
"default": {
add: function add(element, settings, message) {
Expand All @@ -48,8 +51,42 @@
element.removeClass('is-invalid');
errorElement.remove();
}
},
multi_select: {
add: function add(element, settings, message) {
var wrapperElement = element.closest(settings.wrapper_tag + '.' + settings.wrapper_class.replace(/ /g, '.'));
var parentElement = element.parent();
var errorElement = wrapperElement.find(settings.error_tag + '.invalid-feedback');

if (!errorElement.length) {
errorElement = $('<' + settings.error_tag + '>', {
"class": 'invalid-feedback d-block',
text: message
});
parentElement.after(errorElement);
}

wrapperElement.addClass(settings.wrapper_error_class);
element.addClass('is-invalid');
errorElement.text(message);
},
remove: function remove(element, settings) {
var wrapperElement = element.closest(settings.wrapper_tag + '.' + settings.wrapper_class.replace(/ /g, '.'));
var errorElement = wrapperElement.find(settings.error_tag + '.invalid-feedback');
var invalidSiblingExists = element.siblings('.is-invalid').length;

if (!invalidSiblingExists) {
wrapperElement.removeClass(settings.wrapper_error_class);
errorElement.remove();
}

element.removeClass('is-invalid');
}
}
}
};
simpleFormFormBuilder.wrappers.horizontal_multi_select = simpleFormFormBuilder.wrappers.multi_select;
simpleFormFormBuilder.wrappers.vertical_multi_select = simpleFormFormBuilder.wrappers.multi_select;
ClientSideValidations.formBuilders['SimpleForm::FormBuilder'] = simpleFormFormBuilder;

})));
7 changes: 5 additions & 2 deletions dist/simple-form.esm.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,17 @@ import ClientSideValidations from '@client-side-validations/client-side-validati

ClientSideValidations.formBuilders['SimpleForm::FormBuilder'] = {
add: function add(element, settings, message) {
this.wrapper(settings.wrapper).add.call(this, element, settings, message);
this.wrapper(this.wrapperName(element, settings)).add.call(this, element, settings, message);
},
remove: function remove(element, settings) {
this.wrapper(settings.wrapper).remove.call(this, element, settings);
this.wrapper(this.wrapperName(element, settings)).remove.call(this, element, settings);
},
wrapper: function wrapper(name) {
return this.wrappers[name] || this.wrappers["default"];
},
wrapperName: function wrapperName(element, settings) {
return element.data('clientSideValidationsWrapper') || settings.wrapper;
},
wrappers: {
"default": {
add: function add(element, settings, message) {
Expand Down
7 changes: 5 additions & 2 deletions dist/simple-form.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,17 @@

ClientSideValidations.formBuilders['SimpleForm::FormBuilder'] = {
add: function add(element, settings, message) {
this.wrapper(settings.wrapper).add.call(this, element, settings, message);
this.wrapper(this.wrapperName(element, settings)).add.call(this, element, settings, message);
},
remove: function remove(element, settings) {
this.wrapper(settings.wrapper).remove.call(this, element, settings);
this.wrapper(this.wrapperName(element, settings)).remove.call(this, element, settings);
},
wrapper: function wrapper(name) {
return this.wrappers[name] || this.wrappers["default"];
},
wrapperName: function wrapperName(element, settings) {
return element.data('clientSideValidationsWrapper') || settings.wrapper;
},
wrappers: {
"default": {
add: function add(element, settings, message) {
Expand Down
10 changes: 10 additions & 0 deletions lib/client_side_validations/simple_form/form_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ def input(attribute_name, options = {}, &block)
options.delete(:validate)
end

add_field_specific_wrapper_name_to_field_options(attribute_name, options, &block)

super(attribute_name, options, &block)
end

Expand All @@ -34,6 +36,14 @@ def wrapper_error_component
wrapper.find(:full_error)
end
end

def add_field_specific_wrapper_name_to_field_options(attribute_name, options, &block)
wrapper_name = options[:wrapper] || find_wrapper_mapping(find_input(attribute_name, options, &block).input_type)
return if wrapper_name.nil?

options[:input_html] ||= {}
options[:input_html][:'data-client-side-validations-wrapper'] = wrapper_name
end
end
end
end
Expand Down
45 changes: 41 additions & 4 deletions src/main.bootstrap4.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
import $ from 'jquery'
import ClientSideValidations from '@client-side-validations/client-side-validations'

ClientSideValidations.formBuilders['SimpleForm::FormBuilder'] = {
const simpleFormFormBuilder = {
add: function (element, settings, message) {
this.wrapper(settings.wrapper).add.call(this, element, settings, message)
this.wrapper(this.wrapperName(element, settings)).add.call(this, element, settings, message)
},
remove: function (element, settings) {
this.wrapper(settings.wrapper).remove.call(this, element, settings)
this.wrapper(this.wrapperName(element, settings)).remove.call(this, element, settings)
},
wrapper: function (name) {
return this.wrappers[name] || this.wrappers.default
},
wrapperName: function (element, settings) {
return element.data('clientSideValidationsWrapper') || settings.wrapper
},

wrappers: {
default: {
add (element, settings, message) {
const wrapperElement = element.parent()
let errorElement = wrapperElement.find(settings.error_tag + '.invalid-feedback')
var errorElement = wrapperElement.find(settings.error_tag + '.invalid-feedback')

if (!errorElement.length) {
errorElement = $('<' + settings.error_tag + '>', { class: 'invalid-feedback', text: message })
Expand All @@ -36,6 +39,40 @@ ClientSideValidations.formBuilders['SimpleForm::FormBuilder'] = {
element.removeClass('is-invalid')
errorElement.remove()
}
},
multi_select: {
add (element, settings, message) {
const wrapperElement = element.closest(settings.wrapper_tag + '.' + settings.wrapper_class.replace(/ /g, '.'))
const parentElement = element.parent()
var errorElement = wrapperElement.find(settings.error_tag + '.invalid-feedback')

if (!errorElement.length) {
errorElement = $('<' + settings.error_tag + '>', { class: 'invalid-feedback d-block', text: message })
parentElement.after(errorElement)
}

wrapperElement.addClass(settings.wrapper_error_class)
element.addClass('is-invalid')
errorElement.text(message)
},
remove (element, settings) {
const wrapperElement = element.closest(settings.wrapper_tag + '.' + settings.wrapper_class.replace(/ /g, '.'))
const errorElement = wrapperElement.find(settings.error_tag + '.invalid-feedback')

const invalidSiblingExists = element.siblings('.is-invalid').length

if (!invalidSiblingExists) {
wrapperElement.removeClass(settings.wrapper_error_class)
errorElement.remove()
}

element.removeClass('is-invalid')
}
}
}
}

simpleFormFormBuilder.wrappers.horizontal_multi_select = simpleFormFormBuilder.wrappers.multi_select
simpleFormFormBuilder.wrappers.vertical_multi_select = simpleFormFormBuilder.wrappers.multi_select

ClientSideValidations.formBuilders['SimpleForm::FormBuilder'] = simpleFormFormBuilder
9 changes: 6 additions & 3 deletions src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,23 @@ import ClientSideValidations from '@client-side-validations/client-side-validati

ClientSideValidations.formBuilders['SimpleForm::FormBuilder'] = {
add: function (element, settings, message) {
this.wrapper(settings.wrapper).add.call(this, element, settings, message)
this.wrapper(this.wrapperName(element, settings)).add.call(this, element, settings, message)
},
remove: function (element, settings) {
this.wrapper(settings.wrapper).remove.call(this, element, settings)
this.wrapper(this.wrapperName(element, settings)).remove.call(this, element, settings)
},
wrapper: function (name) {
return this.wrappers[name] || this.wrappers.default
},
wrapperName: function (element, settings) {
return element.data('clientSideValidationsWrapper') || settings.wrapper
},

wrappers: {
default: {
add (element, settings, message) {
const wrapper = element.closest(settings.wrapper_tag + '.' + settings.wrapper_class.replace(/ /g, '.'))
let errorElement = wrapper.find(settings.error_tag + '.' + settings.error_class.replace(/ /g, '.'))
var errorElement = wrapper.find(settings.error_tag + '.' + settings.error_class.replace(/ /g, '.'))

if (!errorElement.length) {
errorElement = $('<' + settings.error_tag + '>', { class: settings.error_class, text: message })
Expand Down
30 changes: 28 additions & 2 deletions test/javascript/public/test/form_builders/validateSimpleForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,16 @@ QUnit.module('Validate SimpleForm', {
dataCsv = {
html_settings: {
type: 'SimpleForm::FormBuilder',
error_class: 'error small',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

small presence had a reason:

399f389

Unfortunately this commit didn't come with a test, so we are not explicitly testing this behavior. 🙏🏼

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I see purpose of small is to test if everything works if 2 classes are present. Sorry, I put it back. I was just comparing it with default simple_form setting and thought it should be same.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't worry, the problem is the missing test, or at least a comment should be present

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implemented an explicit test for this use case in CSV: DavyJonesLocker/client_side_validations@a49dd74

Will do the same here

error_class: 'error small', // 'small' class is present to test 399f389
error_tag: 'span',
wrapper_error_class: 'field_with_errors',
wrapper_tag: 'div',
wrapper_class: 'input',
wrapper: 'default'
},
validators: {
'user[name]': { presence: [{ message: 'must be present' }], format: [{ message: 'is invalid', 'with': { options: 'g', source: '\\d+' } }] }
'user[name]': { presence: [{ message: 'must be present' }], format: [{ message: 'is invalid', 'with': { options: 'g', source: '\\d+' } }] },
'user[date_of_birth]': { presence: [{ message: 'must be present' }] }
}
}

Expand All @@ -40,6 +41,12 @@ QUnit.module('Validate SimpleForm', {
type: 'text'
}))
.append($('<label for="user_name">Name</label>'))
.append($('<input />', {
name: 'user[date_of_birth]',
id: 'date_of_birth',
type: 'text',
'data-client-side-validations-wrapper': 'custom_date_wrapper'
}))
$('form#new_user').validate()
}
})
Expand Down Expand Up @@ -82,3 +89,22 @@ QUnit.test('Validate pre-existing error blocks are re-used', function (assert) {
assert.ok(input.parent().find('span.error:contains("is invalid")').length === 1)
assert.ok(form.find('span.error').length === 1)
})

QUnit.test('Validate correct JS Builder\'s wrapper is called for custom_wrapper', function (assert) {
const oldWrappers = $.extend({}, ClientSideValidations.formBuilders['SimpleForm::FormBuilder'].wrappers)

// It would be probably better to use some stub library but I want to keep it simple
let customWrapperCalled = false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, remember no ES6 here

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup sorry, I cherry-picked this from #81, I guess I will remove this commit once you merge/implement #81. Fixed in both #81 and this PR.


ClientSideValidations.formBuilders['SimpleForm::FormBuilder'].wrappers['custom_date_wrapper'] = {
add: function(element, settings, message) { customWrapperCalled=true; },
remove: function(element, settings) {}
}

var form = $('form#new_user');
var input = form.find('input#date_of_birth')
input.trigger('focusout')

assert.ok(customWrapperCalled);
ClientSideValidations.formBuilders['SimpleForm::FormBuilder'].wrappers = oldWrappers
})
Loading