Skip to content

Commit

Permalink
feat(FormElement): Add customWarning property to show warnings indepe…
Browse files Browse the repository at this point in the history
…ndently of validation. Thanks to @elgordino
  • Loading branch information
elgordino authored and simonihmig committed Jun 13, 2017
1 parent 2efe5dd commit 540e233
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 11 deletions.
55 changes: 45 additions & 10 deletions addon/components/base/bs-form/element.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,9 @@ const nonDefaultLayouts = A([
* the `successIcon` feedback icon is displayed if `controlType` is a text field
* the validation messages are removed
In case you want to display some error message that is independent of the model's validation, for example to display
a failure message on a login form after a failed authentication attempt (so not coming from the validation library),
you can use the `customError` property to do so.
In case you want to display some error or warning message that is independent of the model's validation, for
example to display a failure message on a login form after a failed authentication attempt (so not coming from
the validation library), you can use the `customError` or `customWarning` properties to do so.
### Custom controls
Expand Down Expand Up @@ -488,20 +488,42 @@ export default FormGroup.extend({
hasCustomError: computed.notEmpty('customError'),

/**
* The array of validation messages (either errors or warnings) from the `model`'s validation.
* Show a custom warning message that does not come from the model's validation. Will be immediately shown, regardless
* of any user interaction (i.e. no `focusOut` event required). If the model's validation has an error then the error
* will be shown in place of this warning.
*
* @property customWarning
* @type string
* @public
*/
customWarning: null,

/**
* @property hasCustomWarning
* @type boolean
* @readonly
* @private
*/
hasCustomWarning: computed.notEmpty('customWarning'),

/**
* The array of validation messages (either errors or warnings) from either custom error/warnings or , if we are showing model validation messages, the model's validation
*
* @property validationMessages
* @type array
* @private
*/
validationMessages: computed('hasCustomError', 'customError', 'hasErrors', 'hasWarnings', 'errors.[]', 'warnings.[]', function() {
validationMessages: computed('hasCustomError', 'customError', 'hasErrors', 'hasCustomWarning', 'customWarning', 'hasWarnings', 'errors.[]', 'warnings.[]', 'showModelValidation', function() {
if (this.get('hasCustomError')) {
return A([this.get('customError')]);
}
if (this.get('hasErrors')) {
if (this.get('hasErrors') && this.get('showModelValidation')) {
return A(this.get('errors'));
}
if (this.get('hasWarnings')) {
if (this.get('hasCustomWarning')) {
return A([this.get('customWarning')]);
}
if (this.get('hasWarnings') && this.get('showModelValidation')) {
return A(this.get('warnings'));
}
return null;
Expand Down Expand Up @@ -541,7 +563,7 @@ export default FormGroup.extend({
* @default false
* @private
*/
showValidation: computed.or('showOwnValidation', 'showAllValidations', 'hasCustomError'),
showValidation: computed.or('showOwnValidation', 'showAllValidations', 'hasCustomError', 'hasCustomWarning'),

/**
* @property showOwnValidation
Expand All @@ -559,6 +581,14 @@ export default FormGroup.extend({
*/
showAllValidations: false,

/**
* @property showModelValidations
* @type boolean
* @readonly
* @private
*/
showModelValidation: computed.or('showOwnValidation', 'showAllValidations'),

/**
* @property showValidationMessages
* @type boolean
Expand Down Expand Up @@ -617,11 +647,16 @@ export default FormGroup.extend({
* @type string
* @private
*/
validation: computed('hasCustomError', 'hasErrors', 'hasWarnings', 'hasValidator', 'showValidation', 'isValidating', 'disabled', function() {
validation: computed('hasCustomError', 'hasErrors', 'hasCustomWarning', 'hasWarnings', 'hasValidator', 'showValidation', 'showModelValidation', 'isValidating', 'disabled', function() {
if (!this.get('showValidation') || !this.get('hasValidator') || this.get('isValidating') || this.get('disabled')) {
return null;
} else if (this.get('showModelValidation')) {
/* The display of model validation messages has been triggered */
return this.get('hasErrors') || this.get('hasCustomError') ? 'error' : (this.get('hasWarnings') || this.get('hasCustomWarning') ? 'warning' : 'success');
} else {
/* If there are custom errors or warnings these should always be shown */
return this.get('hasCustomError') ? 'error' : 'warning';
}
return this.get('hasErrors') || this.get('hasCustomError') ? 'error' : (this.get('hasWarnings') ? 'warning' : 'success');
}),

/**
Expand Down
4 changes: 4 additions & 0 deletions tests/helpers/bootstrap-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ export function validationErrorClass() {
return versionDependent('has-error', 'has-danger');
}

export function validationWarningClass() {
return 'has-warning';
}

export function placementClassFor(type, placement) {
return versionDependent(placement, `${type}-${placement}`);
}
Expand Down
62 changes: 61 additions & 1 deletion tests/integration/components/bs-form/element-test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { click, find, findAll, fillIn, triggerEvent, focus, blur } from 'ember-native-dom-helpers';
import { moduleForComponent } from 'ember-qunit';
import { formFeedbackClass, test, testBS3, validationErrorClass, formHelpTextClass } from '../../../helpers/bootstrap-test';
import { formFeedbackClass, test, testBS3, validationErrorClass, validationWarningClass, formHelpTextClass } from '../../../helpers/bootstrap-test';
import hbs from 'htmlbars-inline-precompile';
import Ember from 'ember';

Expand Down Expand Up @@ -520,6 +520,66 @@ test('shows custom error immediately', function(assert) {
);
});

test('shows custom warning immediately', function(assert) {
this.set('model', Ember.Object.create({ name: null }));
this.set('warning', 'some warning');
this.render(hbs`
{{bs-form/element property='name' hasValidator=true customWarning=warning model=model}}
`);
assert.ok(
find('.form-group').classList.contains(validationWarningClass()),
'custom warning is shown immediately'
);
assert.equal(find(`.form-group .${formFeedbackClass()}`).textContent.trim(), 'some warning');
Ember.run(() => {
this.set('warning', null);
});
assert.notOk(
find('.form-group').classList.contains(validationWarningClass()),
'form group isn\'t shown as having warning if there aren\'t any'
);
});

test('shows validation errors in preference to custom warning', async function(assert) {
this.set('errors', Ember.A(['Invalid']));
this.set('warning', 'some warning');
this.set('model', Ember.Object.create({ name: null }));
this.render(hbs`
{{bs-form/element property='name' hasValidator=true errors=errors customWarning=warning model=model}}
`);
assert.notOk(
find('.form-group').classList.contains(validationErrorClass()),
'validation errors aren\'t shown before user interaction'
);
assert.ok(
find('.form-group').classList.contains(validationWarningClass()),
'custom warning is shown immediately'
);
assert.equal(find(`.form-group .${formFeedbackClass()}`).textContent.trim(), 'some warning', 'Custom Warning is shown');
await focus('input');
await blur('input');
assert.ok(
find('.form-group').classList.contains(validationErrorClass()),
'validation errors are shown after user interaction when errors are present'
);
assert.notOk(
find('.form-group').classList.contains(validationWarningClass()),
'custom warning is removed when errors are shown'
);
assert.equal(find(`.form-group .${formFeedbackClass()}`).textContent.trim(), 'Invalid', 'Validation error is shown');
Ember.run(() => {
this.set('errors', Ember.A());
});
assert.notOk(
find('.form-group').classList.contains(validationErrorClass()),
'form group isn\'t shown as having errors if there aren\'t any'
);
assert.ok(
find('.form-group').classList.contains(validationWarningClass()),
'custom warning is shown when errors are removed'
);
});

test('events enabling validation rendering are configurable per `showValidationOn` (array)', async function(assert) {
this.set('errors', Ember.A(['Invalid']));
this.set('model', Ember.Object.create({ name: null }));
Expand Down

0 comments on commit 540e233

Please sign in to comment.