Validate expense/reimbursement forms
Adds some general helpers and styles for minimalistic form validation.
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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(), '');
|
||||
// });
|
||||
});
|
||||
|
||||
@@ -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'));
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user