Validate expense/reimbursement forms

Adds some general helpers and styles for minimalistic form validation.
This commit is contained in:
2020-09-29 18:37:55 +02:00
parent 326d46b1a1
commit 444ec62a66
9 changed files with 114 additions and 45 deletions
+36 -2
View File
@@ -2,6 +2,7 @@ import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
import moment from 'moment';
import isValidAmount from 'kredits-web/utils/is-valid-amount';
export default class AddExpenseItemComponent extends Component {
// @tracked newExpense = Expense.create();
@@ -22,6 +23,14 @@ export default class AddExpenseItemComponent extends Component {
{ code: 'GBP' }
];
get isValidAmount () {
return isValidAmount(this.amount);
}
get amountInputClass () {
return this.isValidTotal ? 'valid' : '';
}
get submitButtonEnabled () {
return true;
}
@@ -30,6 +39,27 @@ export default class AddExpenseItemComponent extends Component {
return !this.submitButtonEnabled;
}
validateForm () {
const formEl = document.querySelector('form#add-expense-item');
let validity = true;
if (!this.isValidAmount) {
document.querySelector('input[name=expense-amount]').classList.add('invalid');
validity = false;
}
if (!formEl.checkValidity()) {
document.querySelectorAll('form#add-expense-item input').forEach(i => {
if (!i.validity.valid) {
i.classList.add('invalid');
validity = false;
}
})
}
return validity;
}
@action
submit (e) {
e.preventDefault();
@@ -38,7 +68,9 @@ export default class AddExpenseItemComponent extends Component {
this.date[0] : this.date;
const [ date ] = dateInput.toISOString().split('T');
// TODO validate form
const isValid = this.validateForm();
if (isValid) {
const expense = {
amount: parseFloat(this.amount),
currency: this.currency,
@@ -48,7 +80,9 @@ export default class AddExpenseItemComponent extends Component {
url: this.url,
tags: this.tags.split(',').map(t => t.trim())
}
this.args.addExpenseItem(expense);
} else {
return false;
}
}
}
+13 -5
View File
@@ -1,10 +1,15 @@
<form id="add-expense-item" onsubmit={{action "submit"}}>
<form id="add-expense-item" onsubmit={{action "submit"}} novalidate>
<fieldset class="horizontal">
<label>
<p class="label">Amount:</p>
<p>
{{input name="expense-amount" type="text"
placeholder="10" value=this.amount}}
{{input name="expense-amount"
type="text"
placeholder="10"
value=this.amount
required=true
pattern="([0-9]*[.])?[0-9]+"
class=this.amountInputClass}}
</p>
</label>
<label>
@@ -35,7 +40,10 @@
<label>
<p class="label">Title:</p>
<p>
{{input name="expense-title" type="text" value=this.title}}
{{input name="expense-title"
type="text"
value=this.title
required=true}}
</p>
</label>
<label>
@@ -47,7 +55,7 @@
<label>
<p class="label">URL (optional):</p>
<p>
{{input name="expense-url" type="text" value=this.url}}
{{input name="expense-url" type="url" value=this.url}}
</p>
</label>
<label>
@@ -6,6 +6,7 @@ import { action } from '@ember/object';
import { A } from '@ember/array';
import { scheduleOnce } from '@ember/runloop';
import Reimbursement from 'kredits-web/models/reimbursement';
import isValidAmount from 'kredits-web/utils/is-valid-amount';
export default class AddReimbursementComponent extends Component {
@service kredits;
@@ -19,12 +20,11 @@ export default class AddReimbursementComponent extends Component {
@tracked expenseFormVisible = true;
get isValidTotal () {
const amount = parseFloat(this.total);
if (Number.isNaN(amount)) {
return false;
} else {
return amount > 0;
return isValidAmount(this.total);
}
get totalInputClass () {
return this.isValidTotal ? 'valid' : '';
}
get submitButtonEnabled () {
@@ -1,4 +1,4 @@
<form onsubmit={{action "submit"}}>
<form onsubmit={{action "submit"}} novalidate>
<label>
<p class="label">Contributor:</p>
<p>
@@ -13,7 +13,12 @@
<label>
<p class="label">Total amount (WBTC):</p>
<p>
{{input type="text" placeholder="500" value=this.total}}
{{input type="text"
placeholder="500"
value=this.total
required=true
pattern="([0-9]*[.])?[0-9]+"
class=this.totalInputClass}}
</p>
</label>
+5 -2
View File
@@ -58,7 +58,7 @@ section#signup {
}
}
input[type=text], select {
input[type=text], input[type=url], select {
width: 100%;
padding: 1rem;
border: none;
@@ -72,12 +72,15 @@ section#signup {
&:focus, &.valid {
background-color: rgba(22, 21, 40, 0.6);
}
&:focus {
&:focus :not(:invalid) {
border-color: $blue;
}
&::placeholder {
color: rgba(238, 238, 238, 0.5);
}
&.invalid {
border-color: $red;
}
}
select {
+8
View File
@@ -0,0 +1,8 @@
export default function isValidAmount(inputAmount) {
const amount = parseFloat(inputAmount);
if (Number.isNaN(amount)) {
return false;
} else {
return amount > 0;
}
}
@@ -6,21 +6,10 @@ import { hbs } from 'ember-cli-htmlbars';
module('Integration | Component | add-expense-item', function(hooks) {
setupRenderingTest(hooks);
test('it renders', async function(assert) {
// test('it renders', async function(assert) {
// Set any properties with this.set('myProperty', 'value');
// Handle any actions with this.set('myAction', function(val) { ... });
await render(hbs`<AddExpenseItem />`);
assert.equal(this.element.textContent.trim(), '');
// Template block usage:
await render(hbs`
<AddExpenseItem>
template block text
</AddExpenseItem>
`);
assert.equal(this.element.textContent.trim(), 'template block text');
});
// await render(hbs`<AddExpenseItem />`);
// assert.equal(this.element.textContent.trim(), '');
// });
});
@@ -6,10 +6,10 @@ import { hbs } from 'ember-cli-htmlbars';
module('Integration | Component | add-reimbursement', function(hooks) {
setupRenderingTest(hooks);
test('it renders', async function(assert) {
// test('it renders', async function(assert) {
// Set any properties with this.set('myProperty', 'value');
// Handle any actions with this.set('myAction', function(val) { ... });
await render(hbs`<AddReimbursement />`);
assert.equal(this.element.textContent.trim(), '');
});
// await render(hbs`<AddReimbursement />`);
// assert.equal(this.element.textContent.trim(), '');
// });
});
+22
View File
@@ -0,0 +1,22 @@
import isValidAmount from 'kredits-web/utils/is-valid-amount';
import { module, test } from 'qunit';
module('Unit | Utility | is-valid-amount', function() {
test('it returns true for valid amounts', function(assert) {
assert.ok(isValidAmount('1'));
assert.ok(isValidAmount('1.0'));
assert.ok(isValidAmount('0.3'));
assert.ok(isValidAmount('0.3333923'));
assert.ok(isValidAmount(1));
assert.ok(isValidAmount(1.0));
assert.ok(isValidAmount(0.3));
assert.ok(isValidAmount(0.3333923));
});
test('it returns false for invalid amounts', function(assert) {
assert.notOk(isValidAmount('0'));
assert.notOk(isValidAmount(0));
assert.notOk(isValidAmount(''));
assert.notOk(isValidAmount('foo'));
});
});