Commit a5d0a9d5 authored by S Anand's avatar S Anand

ENH: client side FormHandler validation. Fixes #85

parent 31eee02a
Pipeline #78001 passed with stage
in 3 minutes and 4 seconds
......@@ -39,16 +39,13 @@ The full list of options is below. Simple options can be specified as `data-` at
- strings specify a numeral.js format if the value is a number (you must include numeral.js)
- strings specify a moment.js format if the value is a date (you must include moment.js)
- `editable`: `true` (default) / `false`. When `true`, edit and save buttons appears at end of each row.
- To bind UI input element such as dropdown, datepicker, radio etc., `editable` accepts an object with these keys.
- `input`: **Mandatory**. The type of input element to use. The valid values are checkbox, radio, range, select, and any other legal [HTML form input type](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input).
- `options`: An array of options to select from. **Mandatory** if `input` is either of `select` or `radio`
- `attrs`: To place common attributes such as max, min, placeholder, name etc., on the `input` element.
Example:
`input: 'number', attrs: {step: 10, placeholder: '0 - 1000', name: 'some_name'}` would render as
`<input step=10 placeholder="0 - 1000" name="some_name" />`
To bind UI input element such as dropdown, datepicker, radio etc., `editable` accepts an object with these keys.
- `input`: **Mandatory**. The type of input element to use. The valid values are checkbox, radio, range, select, and any other legal [HTML form input type](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input).
- `options`: An array of options to select from. **Mandatory** if `input` is either of `select` or `radio`
- `attrs`: To place common attributes such as max, min, placeholder, name etc., on the input.
Example: `{placeholder: "Age", max:100}` renders `<input placeholder="Age" max="100">`
- `validationMessage`: The message to be shown when invalid input is entered.
Example: `"Age must be less than 100"`
- `template`: string template / function that renders the cell.
- function accepts an object with these keys:
- `name`: column name
......@@ -174,7 +171,7 @@ Features to be implemented:
## $.formhandler examples
Render a table using the FormHandler at `./data`:
### Render from a FormHandler
```html
<div class="formhandler" data-src="./data"></div>
......@@ -183,22 +180,20 @@ Render a table using the FormHandler at `./data`:
</script>
```
Get data inside formhandler table:
### Access data inside formhandler
```html
<div class="formhandler" data-src="./data"></div>
<script>
$('.formhandler')
.on('load', function(formdata, meta, args, options) {
console.log('data inside formhandler table: ', formdata) // gives data loaded in to formhandler table
.on('load', function(data, meta, args, options) {
console.log('data inside formhandler table: ', data)
})
.formhandler()
</script>
```
Customize cell rendering to display a chart in a cell:
## Draw chart in cell
```html
<div class="formhandler" data-src="./data"></div>
......@@ -217,7 +212,7 @@ Customize cell rendering to display a chart in a cell:
</script>
```
In edit mode, show HTML input bindings like Dropdown, Datepicker, Number fields.. :
### Customize inputs in edit mode
```html
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.6-rc.0/css/select2.min.css"/>
......@@ -241,11 +236,14 @@ In edit mode, show HTML input bindings like Dropdown, Datepicker, Number fields.
name: 'c1',
editable: {
input: 'number',
attrs: { // keys and values in `attrs` will be added as <input type="number" min=10 max=100 placeholder="0 - 100"/>
// keys and values in `attrs` will be added as
// <input type="number" min=10 max=100 placeholder="Age"/>
attrs: {
min: 10,
max: 100,
placeholder: '0 - 100'
}
placeholder: 'Age'
},
validationMessage: 'Age must be between 0-100'
}
},
{
......
......@@ -13,7 +13,7 @@
"lint": "eslint index*.js src && eclint check '**/*.html' '**/*.js' '**/*.css' '**/*.yaml' '**/*.md'",
"build": "rimraf dist && json2module package.json > src/package.js && rollup -c",
"dev": "rimraf dist && json2module package.json > src/package.js && rollup -c -w",
"pretest": "npm run build && browserify -s tape -r tape -o test/tape.js",
"pretest": "npm run lint && npm run build && browserify -s tape -r tape -o test/tape.js",
"server": "npm run pretest && npm run lint && node test/server.js",
"test": "tape test/test-*.js | faucet && node test/server.js puppeteer | tap-merge | faucet",
"test-chrome": "node test/server.js chrome | tap-merge | faucet",
......
......@@ -305,23 +305,28 @@ function editHandler($this, template_data, options, template) {
var edit_btn = $('.edit button', $this)
var add_btn = $('.add button', $this)
if (edit_btn.html().toLowerCase() == 'save') {
edit_btn.html('Edit') // TODO: remove hardcoding of name Edit
add_btn.prop('disabled', false)
var edited_rows = $('.edited-row')
if (edited_rows.length > 0)
$('.loader', $this).removeClass('d-none')
var all_ajax = []
var allRowsValid = true
$.each(edited_rows, function (key, edited_row) {
var data = JSON.parse(edited_row.getAttribute('data-val'))
var rowIndex = edited_row.getAttribute('data-row')
for (key in data) {
// TODO: refactor to identify editable columns other than using data-key attrs on <td> tag
var editable_element = $('td[data-key="' + (remove_quotes(key)) + '"] :input', $(edited_row))
if (editable_element.length) {
data[key] = template_data['data'][rowIndex][key] = editable_element.val()
}
$('td[data-key="' + (remove_quotes(key)) + '"] :input', edited_row).each(function() {
if (this.checkValidity()) {
$(this).removeClass('is-invalid')
data[key] = template_data['data'][rowIndex][key] = $(this).val()
} else {
$(this).addClass('is-invalid')
allRowsValid = false
}
})
}
all_ajax.push(
$.ajax(options.src, {
method: 'PUT',
......@@ -334,10 +339,15 @@ function editHandler($this, template_data, options, template) {
})
)
})
if (!allRowsValid) return
$.when.apply($, all_ajax).then(function () {
$('.loader', $this).addClass('d-none')
edit_btn.html('Edit') // TODO: remove hardcoding of name Edit
add_btn.prop('disabled', false)
if (options.edit.done) options.edit.done()
})
template_data.isEdit = false
render_template('table', template_data, options, $this, template)
} else if (edit_btn.html().toLowerCase() == 'edit') {
......
......@@ -213,6 +213,11 @@ Each template receives these variables:
/>
<% } %>
<% if (isEditable.validationMessage) { %>
<div class="invalid-feedback">
<%- isEditable.validationMessage %>
</div>
<% } %>
<!-- end -->
<!-- var template_page -->
......
......@@ -71,8 +71,10 @@
attrs: {
min: 10,
max: 100,
required: '',
placeholder: '0 - 100'
}
},
validationMessage: 'Number must be between 0-100 and is mandatory'
}
},
{
......@@ -112,9 +114,9 @@
}
},
{
name: 'delete',
name: 'Actions',
template: function(row) {
return "<td><button data-action='delete'><i class='fa fa-trash'></i></button></td>"
return "<td><button data-action='edit'><i class='fa fa-trash'></i></button></td>"
},
}
],
......@@ -144,20 +146,46 @@
$('.edit button').click()
// make sure the initial value is Europe
test.equals($(".edit-fh table > tbody > tr:nth-child(1) > td:nth-child(2) > input").val().trim(), init_cell_value)
$(".edit-fh table > tbody > tr:nth-child(1) > td:nth-child(3) > input").val('300000').trigger('change')
$(".edit-fh table > tbody > tr:nth-child(3) > td:nth-child(3) > input").val('-90').trigger('change')
// save row
$('.edit button').click()
test.equals($('.is-invalid').val(), '300000')
$(".edit-fh table > tbody > tr:nth-child(1) > td:nth-child(3) > input").val('35').trigger('change')
// modify cell value inside <input>
$(".edit-fh table > tbody > tr:nth-child(1) > td:nth-child(2) > input").val('Edited')
$(".edit-fh table > tbody > tr:nth-child(1) > td:nth-child(2) > input").trigger('change')
$(".edit-fh table > tbody > tr:nth-child(1) > td:nth-child(2) > input").val('Edited').trigger('change')
test.equals($(".edit-fh table > tbody > tr:nth-child(1) > td:nth-child(2) > input").val().trim(), 'Edited')
// // save row
$('.edit button').click()
test.equals($('.is-invalid').val(), '-90')
$(".edit-fh table > tbody > tr:nth-child(3) > td:nth-child(3) > input").val('28').trigger('change')
// save row
$('.edit button').click()
setTimeout(function() {
// COMMENTING NEXT TEST CASE: Gramex required
// test.notOk($('.add button').prop('disabled'))
// test.equals($('.edit-btn').text(), 'Edit')
$('.add button').click()
test.ok($('.edit button').prop('disabled'))
test.equals($('div.edit-fh tr.new-row td:nth-child(4) select').length, 1)
// all other columns must be input textbox and editable, overriding isEditable: false option also
test.equals($('div.edit-fh tr.new-row td:nth-child(1) input').length, 1)
test.ok($('.edit button').prop('disabled'))
test.end()
$('.add button').click()
test.equals($('div.edit-fh tr.new-row td:nth-child(4) select').length, 1)
// all other columns must be input textbox and editable, overriding isEditable: false option also
test.equals($('div.edit-fh tr.new-row td:nth-child(1) input').length, 1)
test.end()
}, 500)
/*
......
......@@ -93,9 +93,11 @@
e.which = 13; //choose the one you want
e.keyCode = 13;
$("#city_table table tbody tr:nth-child(1) td:nth-child(1) input").trigger(e);
t.equals($("#city_table .edit button").text(), 'Edit')
$("#city_table tr:nth-child(1) td:nth-child(3) div").click()
t.equals($("#city_table .note").text(), " NOTFIED ×")
setTimeout(function() {
t.equals($("#city_table .edit button").text(), 'Edit')
$("#city_table tr:nth-child(1) td:nth-child(3) div").click()
t.equals($("#city_table .note").text(), " NOTFIED ×")
})
//close notification
// $("#city_table .note span").click()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment