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

Validate competency title #1854

Merged
merged 6 commits into from
Jul 14, 2016
Merged
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
58 changes: 58 additions & 0 deletions app/components/competency-title-editor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import Ember from 'ember';
import { validator, buildValidations } from 'ember-cp-validations';
import ValidationErrorDisplay from 'ilios/mixins/validation-error-display';
import { task } from 'ember-concurrency';

const { Component, RSVP, isPresent } = Ember;
const { Promise } = RSVP;

const Validations = buildValidations({
title: [
validator('presence', true),
validator('length', {
max: 200
}),
],
});

export default Component.extend(Validations, ValidationErrorDisplay, {
didReceiveAttrs(){
this._super(...arguments);
const competency = this.get('competency');
if (isPresent(competency)) {
this.set('title', competency.get('title'));
}
},
title: null,
competency: null,
classNames: ['competency-title-editor'],
tagName: 'span',
save: task(function * (){
this.send('addErrorDisplayFor', 'title');
let {validations} = yield this.validate();

return new Promise((resolve, reject) => {
if (validations.get('isInvalid')) {
reject();
} else {
const competency = this.get('competency');
const title = this.get('title');
if (isPresent(competency)) {
competency.set('title', title);
}
this.send('clearErrorDisplay');
resolve();
}
});

}),
actions: {
revert() {
this.set('title', null);
const competency = this.get('competency');
if (isPresent(competency)) {
this.set('title', competency.get('title'));
}
},
}
});
32 changes: 32 additions & 0 deletions app/components/new-competency.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import Ember from 'ember';
import { validator, buildValidations } from 'ember-cp-validations';
import ValidationErrorDisplay from 'ilios/mixins/validation-error-display';
import { task, timeout } from 'ember-concurrency';

const { Component } = Ember;

const Validations = buildValidations({
title: [
validator('presence', true),
validator('length', {
max: 200
}),
],
});

export default Component.extend(Validations, ValidationErrorDisplay, {
title: null,
classNames: ['new-competency'],
save: task(function * (){
this.send('addErrorDisplayFor', 'title');
let {validations} = yield this.validate();
if (validations.get('isInvalid')) {
return;
}
yield timeout(10);
const title = this.get('title');
yield this.get('add')(title);
this.send('clearErrorDisplay');
this.set('title', null);
})
});
4 changes: 2 additions & 2 deletions app/components/school-competencies-expanded.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export default Component.extend({
}
});
},
addCompetencyToBuffer(title, domain){
addCompetencyToBuffer(domain, title){
let competency = this.get('store').createRecord('competency', {title});
if (isPresent(domain)) {
competency.set('parent', domain);
Expand Down Expand Up @@ -68,8 +68,8 @@ export default Component.extend({
});
bufferedCompetencies.filterBy('isNew').forEach(competency => {
competency.set('school', school);
promises.pushObject(competency.save());
});
promises.pushObjects(bufferedCompetencies.filterBy('hasDirtyAttributes').invoke('save'));
schoolCompetencies.clear();
bufferedCompetencies.forEach(competency=>{
schoolCompetencies.pushObject(competency);
Expand Down
41 changes: 2 additions & 39 deletions app/components/school-competencies-manager.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
import Ember from 'ember';

const { Component, computed, isPresent, isEmpty } = Ember;
const { sort } = computed;
const { Component, computed, isEmpty } = Ember;

export default Component.extend({
competencies: [],
sortCompetenciesBy: ['title'],
sortedCompetencies: sort('competencies', 'sortCompetenciesBy'),
newDomainValue: null,

domains: computed('competencies.[]', function(){
let competencies = this.get('competencies');
Expand All @@ -16,7 +12,7 @@ export default Component.extend({
}
let domains = competencies.filterBy('isDomain');
let objs = domains.uniq().map(domain => {
let domainCompetencies = competencies.filter(competency => competency.get('parent.id') === domain.get('id'));
let domainCompetencies = competencies.filter(competency => competency.belongsTo('parent').id() === domain.get('id'));
return {
domain,
competencies: domainCompetencies.sortBy('title')
Expand All @@ -26,41 +22,8 @@ export default Component.extend({
return objs.sortBy('domain.title');
}),
actions: {
createNewDomain(){
let value = this.get('newDomainValue');
if (isPresent(value)) {
this.attrs.add(value);
this.set('newDomainValue', null);
}
},
createNewCompetencyFromButton(e){
let domainId = parseInt(e.target.value);
let value = this.$(e.target).parent().find('input').val();
let objs = this.get('domains');
let obj = objs.find(obj => parseInt(obj.domain.get('id')) === domainId);
let domain = obj.domain;
if (isPresent(value) && isPresent(domain)) {
this.attrs.add(value, domain);
}

},
createNewCompetencyFromInput(e){
if (e.keyCode === 13) {
let value = e.target.value;
let domainId = parseInt(this.$(e.target).parent().find('button').val());

let objs = this.get('domains');
let obj = objs.find(obj => parseInt(obj.domain.get('id')) === domainId);
let domain = obj.domain;
if (isPresent(value) && isPresent(domain)) {
this.attrs.add(value, domain);
}
}

},
changeCompetencyTitle(value, competency){
competency.set('title', value);
competency.save();
}
}
});
18 changes: 18 additions & 0 deletions app/templates/components/competency-title-editor.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{{#editable-field
value=title
save=(perform save)
close=(action 'revert')
as |isSaving save close|
}}
{{one-way-input
value=title
update=(action (mut title))
onenter=save
onescape=close
disabled=isSaving
focusOut=(action 'addErrorDisplayFor' 'title')
}}
{{/editable-field}}
{{#if (and (v-get this 'title' 'isInvalid') (is-in showErrorsFor 'title'))}}
<span class="validation-error-message">{{v-get this 'title' 'message'}}</span>
{{/if}}
19 changes: 19 additions & 0 deletions app/templates/components/new-competency.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{{one-way-input
value=title
update=(action (mut title))
onenter=(perform save)
onescape=(pipe (action 'removeErrorDisplayFor' 'title') (action (mut title) ''))
focusOut=(action 'addErrorDisplayFor' 'title')
keyDown=(action 'addErrorDisplayFor' 'title')
placeholder=(t 'general.title')
}}
<button class='save text' {{action (perform save)}}>
{{#if save.isRunning}}
{{fa-icon 'spinner' spin=true}}
{{else}}
{{t 'general.add'}}
{{/if}}
</button>
{{#if (and (v-get this 'title' 'isInvalid') (is-in showErrorsFor 'title'))}}
<span class="validation-error-message">{{v-get this 'title' 'message'}}</span>
{{/if}}
20 changes: 5 additions & 15 deletions app/templates/components/school-competencies-manager.hbs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
{{#each domains as |obj|}}
<div class='hierarchical-list-manager'>
{{inplace-text tagName='h4' value=obj.domain.title save='changeCompetencyTitle' condition=obj.domain}}
{{competency-title-editor competency=obj.domain}}
{{#if (eq obj.competencies.length 0)}}
{{fa-icon 'trash' class='remove clickable' click=(action this.attrs.remove obj.domain)}}
{{/if}}
<ul>
{{#each obj.competencies as |competency|}}
<li>
{{inplace-text value=competency.title save='changeCompetencyTitle' condition=competency}}
{{competency-title-editor competency=competency}}
{{#if (eq competency.objectives.length 0)}}
{{fa-icon 'trash' class='remove clickable' click=(action this.attrs.remove competency)}}
{{else}}
Expand All @@ -16,21 +16,11 @@
</li>
{{/each}}
{{#if obj.domain.id}}
<div>
<input onkeyup={{action (action 'createNewCompetencyFromInput')}} />
<button class='save text' value={{obj.domain.id}} onclick={{action (action 'createNewCompetencyFromButton')}}>{{t 'general.add'}}</button>
</div>
{{new-competency add=(action add obj.domain)}}
{{/if}}
</ul>
</div>
{{/each}}

<div>
<label>{{t 'schools.newDomain'}}</label>
{{one-way-input
value=newDomainValue
update=(action (mut newDomainValue))
onenter=(action 'createNewDomain')
}}
<button class='save text' {{action 'createNewDomain'}}>{{t 'general.add'}}</button>
</div>
<h5>{{t 'schools.newDomain'}}</h5>
{{new-competency add=(action add null)}}
34 changes: 34 additions & 0 deletions tests/integration/components/competency-title-editor-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import Ember from 'ember';
import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';
import wait from 'ember-test-helpers/wait';

const { Object } = Ember;

moduleForComponent('competency-title-editor', 'Integration | Component | competency title editor', {
integration: true
});

test('validation errors do not show up initially', function(assert) {
assert.expect(1);
let competency = Object.create({
title: 'test'
});
this.set('competency', competency);
this.render(hbs`{{competency-title-editor competency=competency}}`);
assert.equal(this.$('.validation-error-message').length, 0);
});

test('validation errors show up when saving', function(assert) {
assert.expect(1);
let competency = Object.create({
title: 'test'
});
this.set('competency', competency);
this.render(hbs`{{competency-title-editor competency=competency}}`);
this.$('.content span:eq(0)').click();
this.$('input').val('').change();
this.$('button.done').click();
assert.equal(this.$('.validation-error-message').length, 1);
return wait();
});
42 changes: 42 additions & 0 deletions tests/integration/components/new-competency-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';
import wait from 'ember-test-helpers/wait';

moduleForComponent('new-competency', 'Integration | Component | new competency', {
integration: true
});

test('it renders', function(assert) {
this.render(hbs`{{new-competency}}`);

assert.equal(this.$('input').length, 1);
assert.equal(this.$('button').text().trim(), 'Add');
});

test('save', function(assert) {
assert.expect(1);
this.set('add', (value) => {
assert.equal(value, 'new co');
});
this.render(hbs`{{new-competency add=(action add)}}`);
this.$('input').val('new co').change();
this.$('button').click();

return wait();
});

test('validation errors do not show up initially', function(assert) {
assert.expect(1);

this.render(hbs`{{new-competency}}`);
assert.equal(this.$('.validation-error-message').length, 0);
});

test('validation errors show up when saving', function(assert) {
assert.expect(1);

this.render(hbs`{{new-competency}}`);
this.$('button.save').click();
assert.equal(this.$('.validation-error-message').length, 1);
return wait();
});
Loading