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

Add checkboxes and radios support #83

Open
wants to merge 6 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 @@ -3,3 +3,5 @@
source 'https://rubygems.org'

gemspec

gem 'client_side_validations', github: 'MichalRemis/client_side_validations', branch: 'attach-many-JS-validations'
1 change: 1 addition & 0 deletions client_side_validations-simple_form.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ Gem::Specification.new do |spec|
spec.add_development_dependency 'rubocop-performance', '~> 1.5'
spec.add_development_dependency 'rubocop-rails', '~> 2.5'
spec.add_development_dependency 'simplecov', '~> 0.18.5'
spec.add_development_dependency 'sqlite3', '~> 1.4'

# For QUnit testing
spec.add_development_dependency 'shotgun', '~> 0.9.2'
Expand Down
121 changes: 118 additions & 3 deletions dist/simple-form.bootstrap4.esm.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,103 @@
import $ from 'jquery';
import ClientSideValidations from '@client-side-validations/client-side-validations';

ClientSideValidations.formBuilders['SimpleForm::FormBuilder'] = {
function checkedInputsCount(element) {
var formSettings = element.closest('form[data-client-side-validations]').data('clientSideValidations');
var wrapperClass = formSettings.html_settings.wrapper_class;
return element.closest(".".concat(wrapperClass.replace(/ /g, '.'))).find('input:checked').length;
}

var originalLengthValidator = ClientSideValidations.validators.local.length;
var VALIDATIONS = {
is: function is(a, b) {
return a === parseInt(b, 10);
},
minimum: function minimum(a, b) {
return a >= parseInt(b, 10);
},
maximum: function maximum(a, b) {
return a <= parseInt(b, 10);
}
};

var runValidations = function runValidations(valueLength, options) {
for (var validation in VALIDATIONS) {
var validationOption = options[validation];
var validationFunction = VALIDATIONS[validation];

if (validationOption && !validationFunction(valueLength, validationOption)) {
return options.messages[validation];
}
}
};

ClientSideValidations.validators.local.length = function (element, options) {
if (element.attr('type') === 'checkbox') {
var count = checkedInputsCount(element);

if (options.allow_blank && count === 0) {
return;
}

return runValidations(count, options);
} else {
return originalLengthValidator(element, options);
}
};

var originalPresenceValidator = ClientSideValidations.validators.local.presence;

ClientSideValidations.validators.local.presence = function (element, options) {
if (element.attr('type') === 'checkbox' || element.attr('type') === 'radio') {
if (checkedInputsCount(element) === 0) {
return options.message;
}
} else {
return originalPresenceValidator(element, options);
}
};

// It could be fixed in main CSV if radio_buttons validations are needed there and
// in that case we may removed it from here

var originalInputEnabler = window.ClientSideValidations.enablers.input;

window.ClientSideValidations.enablers.input = function (input) {
originalInputEnabler(input);
var $input = $(input);
var form = input.form;
var eventsToBind = window.ClientSideValidations.eventsToBind.input(form);
var wrapperClass = form.ClientSideValidations.settings.html_settings.wrapper_class;

for (var eventName in eventsToBind) {
var eventFunction = eventsToBind[eventName];
$input.filter(':radio').each(function () {
return $(this).attr('data-validate', true);
}).on(eventName, eventFunction);
}

$input.filter(':radio').on('change.ClientSideValidations', function () {
$(this).isValid(form.ClientSideValidations.settings.validators);
}); // when we change radio/check mark also all sibling radios/checkboxes as changed to revalidate on submit

$input.filter(':radio,:checkbox').on('change.ClientSideValidations', function () {
$(this).closest(".".concat(wrapperClass.replace(/ /g, '.'))).find(':radio,:checkbox').data('changed', true);
});
};

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 +129,34 @@ ClientSideValidations.formBuilders['SimpleForm::FormBuilder'] = {
element.removeClass('is-invalid');
errorElement.remove();
}
},
vertical_collection: {
add: function add(element, settings, message) {
var wrapperElement = element.closest('.' + settings.wrapper_class.replace(/ /g, '.'));
var errorElement = wrapperElement.find(settings.error_tag + '.invalid-feedback');

if (!errorElement.length) {
errorElement = $('<' + settings.error_tag + '>', {
"class": 'invalid-feedback d-block',
text: message
});
element.closest('.form-check').parent().children('.form-check:last').after(errorElement);
element.closest('.form-check').parent().children('.form-check:last').after(errorElement);
}

wrapperElement.addClass(settings.wrapper_error_class);
wrapperElement.find('input:visible').addClass('is-invalid');
errorElement.text(message);
},
remove: function remove(element, settings) {
var wrapperElement = element.closest('.' + settings.wrapper_class.replace(/ /g, '.'));
var errorElement = wrapperElement.find(settings.error_tag + '.invalid-feedback');
wrapperElement.removeClass(settings.wrapper_error_class);
errorElement.remove();
wrapperElement.find('input:visible').removeClass('is-invalid');
}
}
}
};
simpleFormFormBuilder.wrappers.horizontal_collection = simpleFormFormBuilder.wrappers.vertical_collection;
ClientSideValidations.formBuilders['SimpleForm::FormBuilder'] = simpleFormFormBuilder;
121 changes: 118 additions & 3 deletions dist/simple-form.bootstrap4.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,103 @@
$ = $ && Object.prototype.hasOwnProperty.call($, 'default') ? $['default'] : $;
ClientSideValidations = ClientSideValidations && Object.prototype.hasOwnProperty.call(ClientSideValidations, 'default') ? ClientSideValidations['default'] : ClientSideValidations;

ClientSideValidations.formBuilders['SimpleForm::FormBuilder'] = {
function checkedInputsCount(element) {
var formSettings = element.closest('form[data-client-side-validations]').data('clientSideValidations');
var wrapperClass = formSettings.html_settings.wrapper_class;
return element.closest(".".concat(wrapperClass.replace(/ /g, '.'))).find('input:checked').length;
}

var originalLengthValidator = ClientSideValidations.validators.local.length;
var VALIDATIONS = {
is: function is(a, b) {
return a === parseInt(b, 10);
},
minimum: function minimum(a, b) {
return a >= parseInt(b, 10);
},
maximum: function maximum(a, b) {
return a <= parseInt(b, 10);
}
};

var runValidations = function runValidations(valueLength, options) {
for (var validation in VALIDATIONS) {
var validationOption = options[validation];
var validationFunction = VALIDATIONS[validation];

if (validationOption && !validationFunction(valueLength, validationOption)) {
return options.messages[validation];
}
}
};

ClientSideValidations.validators.local.length = function (element, options) {
if (element.attr('type') === 'checkbox') {
var count = checkedInputsCount(element);

if (options.allow_blank && count === 0) {
return;
}

return runValidations(count, options);
} else {
return originalLengthValidator(element, options);
}
};

var originalPresenceValidator = ClientSideValidations.validators.local.presence;

ClientSideValidations.validators.local.presence = function (element, options) {
if (element.attr('type') === 'checkbox' || element.attr('type') === 'radio') {
if (checkedInputsCount(element) === 0) {
return options.message;
}
} else {
return originalPresenceValidator(element, options);
}
};

// It could be fixed in main CSV if radio_buttons validations are needed there and
// in that case we may removed it from here

var originalInputEnabler = window.ClientSideValidations.enablers.input;

window.ClientSideValidations.enablers.input = function (input) {
originalInputEnabler(input);
var $input = $(input);
var form = input.form;
var eventsToBind = window.ClientSideValidations.eventsToBind.input(form);
var wrapperClass = form.ClientSideValidations.settings.html_settings.wrapper_class;

for (var eventName in eventsToBind) {
var eventFunction = eventsToBind[eventName];
$input.filter(':radio').each(function () {
return $(this).attr('data-validate', true);
}).on(eventName, eventFunction);
}

$input.filter(':radio').on('change.ClientSideValidations', function () {
$(this).isValid(form.ClientSideValidations.settings.validators);
}); // when we change radio/check mark also all sibling radios/checkboxes as changed to revalidate on submit

$input.filter(':radio,:checkbox').on('change.ClientSideValidations', function () {
$(this).closest(".".concat(wrapperClass.replace(/ /g, '.'))).find(':radio,:checkbox').data('changed', true);
});
};

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 +135,36 @@
element.removeClass('is-invalid');
errorElement.remove();
}
},
vertical_collection: {
add: function add(element, settings, message) {
var wrapperElement = element.closest('.' + settings.wrapper_class.replace(/ /g, '.'));
var errorElement = wrapperElement.find(settings.error_tag + '.invalid-feedback');

if (!errorElement.length) {
errorElement = $('<' + settings.error_tag + '>', {
"class": 'invalid-feedback d-block',
text: message
});
element.closest('.form-check').parent().children('.form-check:last').after(errorElement);
element.closest('.form-check').parent().children('.form-check:last').after(errorElement);
}

wrapperElement.addClass(settings.wrapper_error_class);
wrapperElement.find('input:visible').addClass('is-invalid');
errorElement.text(message);
},
remove: function remove(element, settings) {
var wrapperElement = element.closest('.' + settings.wrapper_class.replace(/ /g, '.'));
var errorElement = wrapperElement.find(settings.error_tag + '.invalid-feedback');
wrapperElement.removeClass(settings.wrapper_error_class);
errorElement.remove();
wrapperElement.find('input:visible').removeClass('is-invalid');
}
}
}
};
simpleFormFormBuilder.wrappers.horizontal_collection = simpleFormFormBuilder.wrappers.vertical_collection;
ClientSideValidations.formBuilders['SimpleForm::FormBuilder'] = simpleFormFormBuilder;

})));
Loading