diff --git a/addon/components/bs-form/element.js b/addon/components/bs-form/element.js index 675ce97..d7c3339 100755 --- a/addon/components/bs-form/element.js +++ b/addon/components/bs-form/element.js @@ -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() { diff --git a/tests/integration/components/bs-form-element-test.js b/tests/integration/components/bs-form-element-test.js index 9637a95..f859ba2 100755 --- a/tests/integration/components/bs-form-element-test.js +++ b/tests/integration/components/bs-form-element-test.js @@ -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, @@ -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` + + + + `); + + 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` + + + + `); + + 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` + + + + `); + + 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` + + + + `); + + await triggerEvent('form', 'submit'); + assert.dom('.invalid-feedback').doesNotExist(); + }); });