Skip to content
This repository has been archived by the owner on Nov 28, 2022. It is now read-only.

Commit

Permalink
fix double-render issues with validation-state mixin
Browse files Browse the repository at this point in the history
  • Loading branch information
kevinansfield authored and acburdine committed Feb 1, 2017
1 parent 6d4b318 commit faeefae
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 38 deletions.
29 changes: 20 additions & 9 deletions app/mixins/validation-state.js
Original file line number Diff line number Diff line change
@@ -1,34 +1,45 @@
import Mixin from 'ember-metal/mixin';
import computed from 'ember-computed';
import {isEmpty} from 'ember-utils';
import {A as emberA} from 'ember-array/utils';
import observer from 'ember-metal/observer';
import run from 'ember-runloop';

export default Mixin.create({

errors: null,
property: '',
hasValidated: emberA(),

hasError: computed('errors.[]', 'property', 'hasValidated.[]', function () {
hasError: false,

setHasError() {
let property = this.get('property');
let errors = this.get('errors');
let hasValidated = this.get('hasValidated');

// if we aren't looking at a specific property we always want an error class
if (!property && !isEmpty(errors)) {
return true;
if (!property && errors && !errors.get('isEmpty')) {
this.set('hasError', true);
return;
}

// If we haven't yet validated this field, there is no validation class needed
if (!hasValidated || !hasValidated.includes(property)) {
return false;
this.set('hasError', false);
return;
}

if (errors) {
return errors.get(property);
if (errors && !isEmpty(errors.errorsFor(property))) {
this.set('hasError', true);
return;
}

return false;
})
this.set('hasError', false);
},

hasErrorObserver: observer('errors.[]', 'property', 'hasValidated.[]', function () {
run.once(this, 'setHasError');
// this.setHasError();
}).on('init')

});
13 changes: 8 additions & 5 deletions tests/integration/components/gh-navitem-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {describe, it} from 'mocha';
import {setupComponentTest} from 'ember-mocha';
import hbs from 'htmlbars-inline-precompile';
import NavItem from 'ghost-admin/models/navigation-item';
import wait from 'ember-test-helpers/wait';

describe('Integration: Component: gh-navitem', function () {
setupComponentTest('gh-navitem', {
Expand Down Expand Up @@ -100,10 +101,12 @@ describe('Integration: Component: gh-navitem', function () {
this.render(hbs`{{gh-navitem navItem=navItem baseUrl=baseUrl}}`);
let $item = this.$('.gh-blognav-item');

expect($item.hasClass('gh-blognav-item--error')).to.be.true;
expect($item.find('.gh-blognav-label').hasClass('error')).to.be.true;
expect($item.find('.gh-blognav-label .response').text().trim()).to.equal('You must specify a label');
expect($item.find('.gh-blognav-url').hasClass('error')).to.be.true;
expect($item.find('.gh-blognav-url .response').text().trim()).to.equal('You must specify a URL or relative path');
return wait().then(() => {
expect($item.hasClass('gh-blognav-item--error')).to.be.true;
expect($item.find('.gh-blognav-label').hasClass('error')).to.be.true;
expect($item.find('.gh-blognav-label .response').text().trim()).to.equal('You must specify a label');
expect($item.find('.gh-blognav-url').hasClass('error')).to.be.true;
expect($item.find('.gh-blognav-url .response').text().trim()).to.equal('You must specify a URL or relative path');
});
});
});
31 changes: 17 additions & 14 deletions tests/integration/components/gh-tag-settings-form-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import Service from 'ember-service';
import EmberObject from 'ember-object';
import run from 'ember-runloop';
import DS from 'ember-data';
import wait from 'ember-test-helpers/wait';

const {Errors} = DS;

Expand Down Expand Up @@ -195,24 +196,26 @@ describe('Integration: Component: gh-tag-settings-form', function () {
{{gh-tag-settings-form tag=tag setProperty=(action 'setProperty')}}
`);

let nameFormGroup = this.$('input[name="name"]').closest('.form-group');
expect(nameFormGroup.hasClass('error'), 'name form group has error state').to.be.true;
expect(nameFormGroup.find('.response').length, 'name form group has error message').to.equal(1);
return wait().then(() => {
let nameFormGroup = this.$('input[name="name"]').closest('.form-group');
expect(nameFormGroup.hasClass('error'), 'name form group has error state').to.be.true;
expect(nameFormGroup.find('.response').length, 'name form group has error message').to.equal(1);

let slugFormGroup = this.$('input[name="slug"]').closest('.form-group');
expect(slugFormGroup.hasClass('error'), 'slug form group has error state').to.be.true;
expect(slugFormGroup.find('.response').length, 'slug form group has error message').to.equal(1);
let slugFormGroup = this.$('input[name="slug"]').closest('.form-group');
expect(slugFormGroup.hasClass('error'), 'slug form group has error state').to.be.true;
expect(slugFormGroup.find('.response').length, 'slug form group has error message').to.equal(1);

let descriptionFormGroup = this.$('textarea[name="description"]').closest('.form-group');
expect(descriptionFormGroup.hasClass('error'), 'description form group has error state').to.be.true;
let descriptionFormGroup = this.$('textarea[name="description"]').closest('.form-group');
expect(descriptionFormGroup.hasClass('error'), 'description form group has error state').to.be.true;

let metaTitleFormGroup = this.$('input[name="metaTitle"]').closest('.form-group');
expect(metaTitleFormGroup.hasClass('error'), 'metaTitle form group has error state').to.be.true;
expect(metaTitleFormGroup.find('.response').length, 'metaTitle form group has error message').to.equal(1);
let metaTitleFormGroup = this.$('input[name="metaTitle"]').closest('.form-group');
expect(metaTitleFormGroup.hasClass('error'), 'metaTitle form group has error state').to.be.true;
expect(metaTitleFormGroup.find('.response').length, 'metaTitle form group has error message').to.equal(1);

let metaDescriptionFormGroup = this.$('textarea[name="metaDescription"]').closest('.form-group');
expect(metaDescriptionFormGroup.hasClass('error'), 'metaDescription form group has error state').to.be.true;
expect(metaDescriptionFormGroup.find('.response').length, 'metaDescription form group has error message').to.equal(1);
let metaDescriptionFormGroup = this.$('textarea[name="metaDescription"]').closest('.form-group');
expect(metaDescriptionFormGroup.hasClass('error'), 'metaDescription form group has error state').to.be.true;
expect(metaDescriptionFormGroup.find('.response').length, 'metaDescription form group has error message').to.equal(1);
});
});

it('displays char count for text fields', function () {
Expand Down
33 changes: 23 additions & 10 deletions tests/integration/components/gh-validation-status-container-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {setupComponentTest} from 'ember-mocha';
import hbs from 'htmlbars-inline-precompile';
import EmberObject from 'ember-object';
import DS from 'ember-data';
import wait from 'ember-test-helpers/wait';

const {Errors} = DS;

Expand All @@ -27,9 +28,12 @@ describe('Integration: Component: gh-validation-status-container', function () {
{{#gh-validation-status-container class="gh-test" property="name" errors=testObject.errors hasValidated=testObject.hasValidated}}
{{/gh-validation-status-container}}
`);
expect(this.$('.gh-test')).to.have.length(1);
expect(this.$('.gh-test').hasClass('success')).to.be.false;
expect(this.$('.gh-test').hasClass('error')).to.be.false;

return wait().then(() => {
expect(this.$('.gh-test')).to.have.length(1);
expect(this.$('.gh-test').hasClass('success')).to.be.false;
expect(this.$('.gh-test').hasClass('error')).to.be.false;
});
});

it('has success class when valid', function () {
Expand All @@ -39,9 +43,12 @@ describe('Integration: Component: gh-validation-status-container', function () {
{{#gh-validation-status-container class="gh-test" property="name" errors=testObject.errors hasValidated=testObject.hasValidated}}
{{/gh-validation-status-container}}
`);
expect(this.$('.gh-test')).to.have.length(1);
expect(this.$('.gh-test').hasClass('success')).to.be.true;
expect(this.$('.gh-test').hasClass('error')).to.be.false;

return wait().then(() => {
expect(this.$('.gh-test')).to.have.length(1);
expect(this.$('.gh-test').hasClass('success')).to.be.true;
expect(this.$('.gh-test').hasClass('error')).to.be.false;
});
});

it('has error class when invalid', function () {
Expand All @@ -52,9 +59,12 @@ describe('Integration: Component: gh-validation-status-container', function () {
{{#gh-validation-status-container class="gh-test" property="name" errors=testObject.errors hasValidated=testObject.hasValidated}}
{{/gh-validation-status-container}}
`);
expect(this.$('.gh-test')).to.have.length(1);
expect(this.$('.gh-test').hasClass('success')).to.be.false;
expect(this.$('.gh-test').hasClass('error')).to.be.true;

return wait().then(() => {
expect(this.$('.gh-test')).to.have.length(1);
expect(this.$('.gh-test').hasClass('success')).to.be.false;
expect(this.$('.gh-test').hasClass('error')).to.be.true;
});
});

it('still renders if hasValidated is undefined', function () {
Expand All @@ -64,6 +74,9 @@ describe('Integration: Component: gh-validation-status-container', function () {
{{#gh-validation-status-container class="gh-test" property="name" errors=testObject.errors hasValidated=testObject.hasValidated}}
{{/gh-validation-status-container}}
`);
expect(this.$('.gh-test')).to.have.length(1);

return wait().then(() => {
expect(this.$('.gh-test')).to.have.length(1);
});
});
});

0 comments on commit faeefae

Please sign in to comment.