Skip to content

Commit

Permalink
fix(form): add proper table validation
Browse files Browse the repository at this point in the history
  • Loading branch information
Jonas Metzener authored and anehx committed Dec 27, 2019
1 parent 2305df3 commit 7c6787b
Show file tree
Hide file tree
Showing 8 changed files with 112 additions and 101 deletions.
15 changes: 13 additions & 2 deletions addon/components/cf-field/input/table.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export default Component.extend({
// Remove orphand document from Caluma store.
this.calumaStore.delete(document.pk);

this.set("showDeleteModal", false);
this.closeModal("showDeleteModal");
}),

save: task(function*() {
Expand All @@ -97,7 +97,7 @@ export default Component.extend({
);
}

this.set("showAddModal", false);
this.closeModal("showAddModal");
} catch (e) {
// eslint-disable-next-line no-console
console.error(e);
Expand All @@ -107,13 +107,24 @@ export default Component.extend({
}
}),

closeModal(propertyName) {
this.set(propertyName, false);

this.field.validate.perform();
},

actions: {
closeModal(propertyName) {
this.closeModal(propertyName);
},

editRow(document) {
this.setProperties({
documentToEdit: document,
showAddModal: true
});
},

deleteRow(document) {
this.setProperties({
documentToDelete: document,
Expand Down
26 changes: 24 additions & 2 deletions addon/lib/field.js
Original file line number Diff line number Diff line change
Expand Up @@ -715,8 +715,30 @@ export default Base.extend({
* @return {RSVP.Promise}
* @private
*/
_validateTableQuestion() {
return resolve(true);
async _validateTableQuestion() {
if (!this.value) return true;

const rowValidations = await all(
this.value.map(async row => {
const validFields = await all(
row.fields.map(async field => {
await field.validate.perform();

return field.isValid;
})
);

return validFields.every(Boolean);
})
);

return (
rowValidations.every(Boolean) || {
type: "table",
context: {},
value: null
}
);
},

/**
Expand Down
6 changes: 3 additions & 3 deletions addon/templates/components/cf-field/input/table.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@

{{#uk-modal
visible=(and showAddModal documentToEdit)
on-hide=(action (mut showAddModal) false)
on-hide=(action "closeModal" "showAddModal")
btnClose=true
bgClose=false
as |modal|}}
Expand Down Expand Up @@ -72,10 +72,10 @@

{{#uk-modal
visible=(and showDeleteModal documentToDelete)
on-hide=(action (mut showDeleteModal) false)
on-hide=(action "closeModal" "showDeleteModal")
btnClose=true
bgClose=false
}}
as |modal|}}
{{#modal.body}}
{{t "caluma.form.deleteRow"}}
{{/modal.body}}
Expand Down
24 changes: 22 additions & 2 deletions tests/unit/lib/data.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const form = {
node: {
slug: "question-2",
label: "Question 2",
isRequired: "false",
isRequired: "'question-1'|answer == 'require-question-2'",
isHidden: "!('question-1'|answer == 'show-question-2')",
__typename: "TextQuestion"
}
Expand Down Expand Up @@ -60,6 +60,26 @@ const form = {

const answers = {
edges: [
{
node: {
id: id("StringAnswer"),
question: {
slug: "question-1"
},
stringValue: "test answer",
__typename: "StringAnswer"
}
},
{
node: {
id: id("StringAnswer"),
question: {
slug: "question-2"
},
stringValue: "test answer 2",
__typename: "StringAnswer"
}
},
{
node: {
id: id("TableAnswer"),
Expand All @@ -77,7 +97,7 @@ const answers = {
node: {
slug: "table-form-question",
label: "Question",
isRequired: "false",
isRequired: "true",
isHidden: "false",
__typename: "TextQuestion"
}
Expand Down
139 changes: 47 additions & 92 deletions tests/unit/lib/field-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { setupTest } from "ember-qunit";
import { settled } from "@ember/test-helpers";
import ValidatorServiceStub from "dummy/tests/helpers/validator-service-stub";
import { setupIntl } from "ember-intl/test-support";
import data from "./data";
import { parseDocument } from "ember-caluma/lib/parsers";

module("Unit | Library | field", function(hooks) {
setupTest(hooks);
Expand All @@ -11,112 +13,52 @@ module("Unit | Library | field", function(hooks) {
hooks.beforeEach(async function() {
this.owner.register("service:validator", ValidatorServiceStub);

const question = {
__typename: "TextQuestion",
slug: "test-question",
label: "Test Question",
isHidden: "false",
isRequired: "true"
};

const question2 = {
__typename: "TextQuestion",
slug: "test-question-2",
label: "Test Question 2",
isHidden: "'test-question'|answer == 'hidequestion2'",
isRequired: "'test-question'|answer == 'requirequestion2'"
};

const question3 = {
__typename: "TextQuestion",
slug: "test-question-3",
label: "Test Question 3",
isHidden: "false",
isRequired: "false"
};

const answer = {
__typename: "StringAnswer",
id: btoa(`Answer:xxxx-xxxx`),
stringValue: "test answer",
question: { slug: question.slug }
};

const answer2 = {
__typename: "StringAnswer",
id: btoa(`Answer:yyyy-yyyy`),
stringValue: "test answer 2",
question: { slug: question2.slug }
};

const form = {
__typename: "Form",
slug: "test-form",
name: "Test Form",
questions: [question, question2, question3]
};

const document = this.owner.factoryFor("caluma-model:document").create({
raw: {
__typename: "Document",
id: btoa(`Document:xxxx-xxxx`),
rootForm: form,
forms: [form],
answers: [answer, answer2]
}
});

this.setProperties({
question,
question2,
question3,
answer,
answer2,
document
});
this.set(
"document",
this.owner
.factoryFor("caluma-model:document")
.create({ raw: parseDocument(data) })
);

await settled();
});

test("computes a pk", async function(assert) {
assert.expect(1);

const field = this.document.findField("test-question");
const field = this.document.findField("question-1");

assert.equal(field.pk, "Document:xxxx-xxxx:Question:test-question");
assert.equal(field.pk, `${this.document.pk}:Question:question-1`);
});

test("computes isNew correctly", async function(assert) {
assert.expect(3);

const answeredField = this.document.findField("test-question");
const unansweredField = this.document.findField("test-question-3");
const answeredField = this.document.findField("question-1");
const unansweredField = this.document.findField("question-3");

assert.equal(answeredField.isNew, false);
assert.equal(unansweredField.isNew, true);

const oldValue = answeredField.value;
answeredField.set("answer.value", null); // empty value
answeredField.set("answer.value", null);

assert.equal(answeredField.isNew, true);

answeredField.set("answer.value", oldValue); // reset value
});

test("can compute the question", async function(assert) {
assert.expect(2);

const field = this.document.findField("test-question");
const field = this.document.findField("question-1");

assert.equal(field.question.slug, "test-question");
assert.equal(field.question.label, "Test Question");
assert.equal(field.question.slug, "question-1");
assert.equal(field.question.label, "Question 1");
});

test("can compute the answer", async function(assert) {
assert.expect(4);

const field = this.document.findField("test-question");
const fieldWithoutAnswer = this.document.findField("test-question-3");
const field = this.document.findField("question-1");
const fieldWithoutAnswer = this.document.findField("question-3");

assert.equal(field.answer.value, "test answer");
assert.equal(fieldWithoutAnswer.answer.value, null);
Expand All @@ -127,54 +69,51 @@ module("Unit | Library | field", function(hooks) {
test("it computes optional", async function(assert) {
assert.expect(2);

const dependsOnField = this.document.findField("test-question");
const field = this.document.findField("test-question-2");
const dependsOnField = this.document.findField("question-1");
const field = this.document.findField("question-2");

dependsOnField.set("answer.value", "somevalue");
assert.equal(field.optional, true);

dependsOnField.set("answer.value", "requirequestion2");
dependsOnField.set("answer.value", "require-question-2");
assert.equal(field.optional, false);
});

test("it computes hidden", async function(assert) {
assert.expect(3);
assert.expect(2);

const dependsOnField = this.document.findField("test-question");
const field = this.document.findField("test-question-2");
const dependsOnField = this.document.findField("question-1");
const field = this.document.findField("question-2");

dependsOnField.set("answer.value", "somevalue");
assert.equal(field.hidden, false);
assert.equal(field.hidden, true);

dependsOnField.set("answer.value", "someothervalue");
dependsOnField.set("answer.value", "show-question-2");
assert.equal(field.hidden, false);

dependsOnField.set("answer.value", "hidequestion2");
assert.equal(field.hidden, true);
});

test("it computes hiddenDependencies based on 'answer' transform", async function(assert) {
assert.expect(1);

const field = this.document.findField("test-question-2");
const dependentField = this.document.findField("test-question");
const field = this.document.findField("question-2");
const dependentField = this.document.findField("question-1");

assert.deepEqual(field.hiddenDependencies, [dependentField]);
});

test("it computes optionalDependencies based on 'answer' transform", async function(assert) {
assert.expect(1);

const field = this.document.findField("test-question-2");
const dependentField = this.document.findField("test-question");
const field = this.document.findField("question-2");
const dependentField = this.document.findField("question-1");

assert.deepEqual(field.optionalDependencies, [dependentField]);
});

test("it can handle newlines in JEXL expressions", async function(assert) {
assert.expect(2);

const field = this.document.findField("test-question-2");
const field = this.document.findField("question-2");

const whitespaced = "(\n 1 == 1\r &&\r 2 == 2\n)";

Expand Down Expand Up @@ -431,4 +370,20 @@ module("Unit | Library | field", function(hooks) {
't:caluma.form.validation.inclusion:("in":["option-1"],"value":"other-invalid-option")'
]);
});

test("it can validate table fields", async function(assert) {
assert.expect(2);

const table = this.document.findField("table");
const row = table.value[0];

await table.validate.perform();
assert.deepEqual(table.errors, []);

row.findField("table-form-question").set("answer.value", null);
await table.validate.perform();
assert.deepEqual(table.errors, [
't:caluma.form.validation.table:("value":null)'
]);
});
});
1 change: 1 addition & 0 deletions translations/de.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ caluma:
inclusion: "'{value}' ist kein gültiger Wert für dieses Feld"
uploadFailed: "Beim Hochladen ist ein Fehler aufgetreten."
format: "{errorMsg}"
table: "Mindestens eine Zeile der Tabelle wurde nicht korrekt ausgefüllt"

pikaday:
month-previous: "Vorheriger Monat"
Expand Down
1 change: 1 addition & 0 deletions translations/en.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ caluma:
inclusion: "'{value}' is not a valid value for this field"
uploadFailed: "An error occured during upload."
format: "{errorMsg}"
table: "At least one row of the table was not filled in correctly"

pikaday:
month-previous: "Previous month"
Expand Down
1 change: 1 addition & 0 deletions translations/fr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ caluma:
inclusion: "'{value}' n'est pas une valeur valide pour ce champ"
uploadFailed: "Une erreur s'est produite pendant le téléchargement."
format: "{errorMsg}"
table: "Au moins une ligne du tableau n'a pas été remplie correctement"

pikaday:
month-previous: "Mois précédent"
Expand Down

0 comments on commit 7c6787b

Please sign in to comment.