Skip to content

Commit

Permalink
support multiple validation errors (#29)
Browse files Browse the repository at this point in the history
  • Loading branch information
basz authored Aug 13, 2020
1 parent 3cc29fd commit c88e2eb
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 5 deletions.
28 changes: 26 additions & 2 deletions addon/components/bs-form/element.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,38 @@
import BsFormElement from 'ember-bootstrap/components/bs-form/element';
import { action, get } from '@ember/object';
import { dependentKeyCompat } from '@ember/object/compat';
import { isNone, typeOf } from '@ember/utils';

export default class BsFormElementWithChangesetValidationsSupport extends BsFormElement {
'__ember-bootstrap_subclass' = true;

// We convert
//
// `model.error.${this.property}.validation` which could be either a string or an array
// see https://github.com/validated-changeset/validated-changeset/#error
//
// into
//
// Ember Bootstrap expects errors property of FormElement to be an array of validation messages:
// see https://www.ember-bootstrap.com/api/classes/Components.FormElement.html#property_errors
//
// If the if the property is valid but no validation is present `model.error.[this.property] could also be undefined.
@dependentKeyCompat
get errors() {
let error = get(this, `model.error.${this.property}.validation`);
return error ? [error] : [];
let errors = get(this, `model.error.${this.property}.validation`);

// no messages
if (isNone(errors)) {
return [];
}

// a single messages
if (typeOf(errors) === 'string') {
return [errors];
}

// assume it's an array of messages
return errors;
}

get hasValidator() {
Expand Down
108 changes: 105 additions & 3 deletions tests/integration/components/bs-form-element-test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render, triggerEvent, fillIn, focus, blur } from '@ember/test-helpers';
import { render, triggerEvent, fillIn, focus, blur, findAll } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';
import {
validatePresence,
Expand Down Expand Up @@ -213,8 +213,110 @@ module('Integration | Component | bs form element', function(hooks) {
assert.dom('input').doesNotHaveClass('is-invalid');

await triggerEvent('form', 'submit');
assert.dom('input').doesNotHaveClass('is-valid');
assert.dom('input').doesNotHaveClass('is-invalid');
assert.dom('input')
.doesNotHaveClass('is-valid');
assert.dom('input')
.doesNotHaveClass('is-invalid');
assert.verifySteps(['submit action has been called']);
});

test('invalid-feedback is shown from single validation', async function (assert) {
let model = {
name: '',
};

this.set('model', model);
this.set('validation', {
name: validatePresence(true)
});

await render(hbs`
<BsForm @model={{changeset this.model this.validation}} as |form|>
<form.element @label="Name" @property="name" />
</BsForm>
`);

await triggerEvent('form', 'submit');
assert.dom('.invalid-feedback')
.hasText('Name can\'t be blank');
});

test('invalid-feedback is shown in order from multiple validations', async function (assert) {
let model = {
name: '',
};

this.set('model', model);
this.set('validation', {
name: [
validatePresence(true),
validateLength({ min: 4 })
]
});

await render(hbs`
<BsForm @model={{changeset this.model this.validation}} as |form|>
<form.element @label="Name" @property="name" />
</BsForm>
`);

await triggerEvent('form', 'submit');
assert.dom('.invalid-feedback')
.hasText('Name can\'t be blank');

await fillIn('input', 'R');
await triggerEvent('form', 'submit');
assert.dom('.invalid-feedback')
.hasText('Name is too short (minimum is 4 characters)');
});

test('invalid-feedback is shown (multiple messages) in order from multiple validations', async function (assert) {
let model = {
name: '',
};

this.set('model', model);
this.set('validation', {
name: [
validatePresence(true),
validateLength({ min: 4 })
]
});

await render(hbs`
<BsForm @model={{changeset this.model this.validation}} as |form|>
<form.element @label="Name" @property="name" @showMultipleErrors={{true}}/>
</BsForm>
`);

await triggerEvent('form', 'submit');

let feedbackElements = findAll('.invalid-feedback');
let results = Array.from(feedbackElements, element => element.textContent.trim())
let expected = ["Name can't be blank", "Name is too short (minimum is 4 characters)"];

expected.forEach((message) => {
assert.ok(results.includes(message))
})
});

test('no feedback is shown for nonexistant validations', async function (assert) {
let model = {
name: '',
};

this.set('model', model);
this.set('validation', {
nombre: validatePresence(true)
});

await render(hbs`
<BsForm @model={{changeset this.model this.validation}} as |form|>
<form.element @label="Name" @property="name" />
</BsForm>
`);

await triggerEvent('form', 'submit');
assert.dom('.invalid-feedback').doesNotExist();
});
});

0 comments on commit c88e2eb

Please sign in to comment.