Commit 02543946 authored by S Anand's avatar S Anand
Browse files

Merge edits

parent a348822b
Pipeline #46610 passed with stage
in 3 minutes and 6 seconds
......@@ -281,10 +281,17 @@ The full list of options is below. Simple options can be specified as `data-` at
- `name`: column name. `"*"` is a special column placeholder for "all columns"
- `title`: for header display. Defaults to the same value as `name`
- `type`: `text` (default) / `number` / `date`. Data type. Determines filters to be used
- `format`: string / function that renders the cell contents.
- functions are applied to the value and the return value is used
- `format`: string / function that returns formatted value.
- function(row, data) returns formatted value
- 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)
- `template`: string template / function that renders the cell.
- function accepts an object with these keys:
- `value`: cell data value
- `format`: cell display value
- `link`: cell link value (if applicable)
- `data`: the dataset from `src`
- string template can use the above variables
- `sort`: `true` / `false` / operators dict with:
- `{'': 'Sort ascending', '-': 'Sort descending'}` (default)
- `filters`: `true` (default) / `false` / operators dict with:
......@@ -298,7 +305,10 @@ The full list of options is below. Simple options can be specified as `data-` at
Example: `function(row) { return 'https://example.org/city/' + row.city }`
- `hideable`: `true` (default) / `false`. Hides the column
- `unique`: TODO: {dict of query parameter and display value} or [list of values] or function?
- `table`: Shows the table control. Can be `true` (default) / `false`
- `table`: Shows the table control. Can be:
- `true`: displays a table (default)
- `'grid'`: renders a grid instead of a table
- `false`: disables the table (and shows nothing for the main content)
- `count`: Shows the number of rows. Can be `true` (default) / `false`
- `page`: Shows the page control. Can be `true` (default) / `false`.
- `pageSize`: page size. Defaults to 100
......@@ -338,12 +348,16 @@ with a simple input. Available template strings are:
- `exportTemplate`
- `filtersTemplate`
- `searchTemplate`
- `rowTemplate`, which can be a string template or function
- function accepts an object with these keys:
- `row`: row data
- `data`: the dataset from `src`
- string template can use the above variables
Features to be implemented:
- Loading indicator
- Full text search
- URL prefix / namespace, if there are multiple tables on the same page
- URL targets other than '#', e.g. pushState
### $.formhandler events
......@@ -356,21 +370,35 @@ Features to be implemented:
### $.formhandler examples
Add a simple table using the FormHandler at `./data` that shows specific columns
with a page size of 10 rows, and does not show the export filter.
Render a table using the FormHandler at `./data`:
```html
<div class="formhandler"
src="./data"
data-columns="id,country,state,sales"
data-page-size="10"
data-export="false"
></div>
<div class="formhandler" data-src="./data"></div>
<script>
$('.formhandler').formhandler()
</script>
```
Customize cell rendering to display a chart in a cell:
```html
<div class="formhandler" data-src="./data"></div>
<script>
$('.formhandler').formhandler({
columns: [
{name: '*'},
{
name: 'c1',
format: function (o) {
return '<svg height="10" width="10"><circle cx="5" cy="5" r="' + o.c1 / 10 + '" fill="red"></circle></svg>'
}
}
}
})
</script>
```
## $.template
......
......@@ -6,7 +6,7 @@ import { parse } from './url.js'
import { namespace } from './namespace_util.js'
// Render components in this order. The empty component is the root component.
var components = ['', 'table', 'page', 'size', 'count', 'export', 'filters', 'error']
var components = ['', 'table', 'page', 'size', 'count', 'export', 'filters', 'error', 'table_grid']
var default_options = {
table: true,
page: true,
......@@ -81,9 +81,8 @@ export function formhandler(js_options) {
})
function draw_table(data, args, meta) {
// Add metadata
meta.rows = data.length,
meta.rows = data.length
meta.columns = data.length ? _.map(data[0], function (val, col) { return { name: col } }) : []
// Assumption: Column name will not be '*'
......@@ -95,8 +94,13 @@ export function formhandler(js_options) {
// Render all components into respective targets
var template_data = {
data: data, meta: meta, args: args, options: options, idcount: 0, parse: parse,
col_defaults: col_defaults,
data: data,
meta: meta,
args: args,
options: options,
idcount: 0,
parse: parse,
col_defaults: col_defaults
}
// Store template_data in $this
$this.data('formhandler', template_data)
......@@ -111,6 +115,8 @@ export function formhandler(js_options) {
target = $this
else {
// Rest are rendered into .<component-name> under $this
if (options[name] == 'grid') name = 'table_grid'
var selector = options[name + 'Target'] || '.' + name
target = $(selector, $this)
// But if they don't exist, treat the selector as a global selecctor
......
......@@ -25,7 +25,7 @@ Each template receives these variables:
<div class="export"></div>
</div>
</div>
<div class="table"></div>
<div class="<%- (options.table == 'grid') ? 'table_grid' : 'table' %>"></div>
</div>
<div class="loader pos-cc d-none">
<div class="fa fa-spinner fa-spin fa-3x fa-fw"></div>
......@@ -59,6 +59,11 @@ Each template receives these variables:
var cols = options.columns.length ? filtered_cols : meta.columns
var form_id = idcount
%>
<% if(options.rowTemplate) { %>
<% _.each(data, function(row) { %>
<%= typeof options.rowTemplate == 'function' ? options.rowTemplate(row, data) : options.rowTemplate %>
<% }) %>
<% } else {%>
<table class="table table-sm table-striped">
<thead>
<% _.each(cols, function(colinfo) {
......@@ -97,31 +102,32 @@ Each template receives these variables:
<% _.each(data, function(row) { %>
<tr>
<% _.each(cols, function(colinfo) { %>
<td>
<% var fmt = typeof(colinfo.format),
val = row[colinfo.name],
disp = fmt == "function" ?
colinfo.format(val) :
fmt === "string" && colinfo.type === "number" ?
numeral(val).format(colinfo.format) :
fmt === "string" && colinfo.type === "date" ?
moment(val).format(colinfo.format):
val %>
<% if ('link' in colinfo) {
var col_link = typeof colinfo.link == 'function' ? colinfo.link(val) : _.template(colinfo.link)(row)
%>
<a href="<%- col_link %>" target="_blank"><%- disp %></a>
<% var fmt = typeof(colinfo.format),
val = row[colinfo.name],
disp = fmt == "function" ?
colinfo.format(row, data) :
fmt === "string" && colinfo.type === "number" ?
numeral(val).format(colinfo.format) :
fmt === "string" && colinfo.type === "date" ?
moment(val).format(colinfo.format):
val,
col_link %>
<% if('link' in colinfo) col_link = typeof colinfo.link == 'function' ? colinfo.link(row) : _.template(colinfo.link)(row) %>
<% if(colinfo.template) { %>
<%= typeof colinfo.template == 'function' ? colinfo.template(val, disp, col_link, data) : colinfo.template %>
<% } else if (col_link) { %>
<td><a href="<%- col_link %>" target="_blank"><%= disp %></a></td>
<% } else { %>
<a class="urlfilter" href="?<%- colinfo.name %>=<%- val %>&amp;_offset=">
<%- disp %>
</a>
<td><a class="urlfilter" href="?<%- colinfo.name %>=<%- val %>&amp;_offset=">
<%= disp %>
</a></td>
<% } %>
</td>
<% }) %>
</tr>
<% }) %>
</tbody>
</table>
<% } %>
<!-- end -->
<!-- var template_page -->
......@@ -248,3 +254,59 @@ Each template receives these variables:
<p class="text-center"><%- message %> </p>
</div>
<!-- end -->
<!-- var template_table_grid -->
<%
var filtered_cols = args['_c'] && args['_c'].length != options.columns.length ?
options.columns.filter(function(col) { return args['_c'].indexOf('-' + col.name) < 0 }) :
options.columns
var cols = options.columns.length ? filtered_cols : meta.columns
var form_id = idcount
var img = (options.icon) ? options.icon : 'http://icons.iconarchive.com/icons/mazenl77/NX11/256/Folder-Default-icon.png'
%>
<% if(options.rowTemplate) { %>
<% _.each(data, function(row) { %>
<%= typeof options.rowTemplate == 'function' ? options.rowTemplate(row, data) : options.rowTemplate %>
<% }) %>
<% } else {%>
<div class="formhandler-grid row">
<% _.each(data, function(row) { %>
<div class="col-sm-3 <%= options.classes || 'formhandler-grid-cell d-inline-block p-3 box-shadow' %>">
<div class="thumbnail">
<% if (img.indexOf('fa ') >= 0) { %>
<i class="<%= img %>"></i>
<% } else { %>
<img class="img img-responsive" src="<%= img %>"/>
<% } %>
<div class="caption">
<% _.each(cols, function(colinfo) { %>
<% var fmt = typeof(colinfo.format),
val = row[colinfo.name],
disp = fmt == "function" ?
colinfo.format(row, data) :
fmt === "string" && colinfo.type === "number" ?
numeral(val).format(colinfo.format) :
fmt === "string" && colinfo.type === "date" ?
moment(val).format(colinfo.format):
val %>
<div>
<strong><%= colinfo.name %></strong>:
<% if ('link' in colinfo) {
var col_link = typeof colinfo.link == 'function' ? colinfo.link(val) : _.template(colinfo.link)(row)
%>
<a href="<%- col_link %>" target="_blank"><%= disp %></a>
<% } else { %>
<a class="urlfilter" href="?<%- colinfo.name %>=<%- val %>&amp;_offset=">
<%= disp %>
</a>
<% } %>
</div>
<% }) %>
</div>
</div>
</div>
<% }) %>
</div>
<% } %>
<!-- end -->
<!DOCTYPE html>
<html>
<head>
<title>formhandler tests</title>
<link rel="stylesheet" href="../node_modules/bootstrap/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="../node_modules/font-awesome/css/font-awesome.min.css">
<style>
.position-relative {
position: relative;
}
.pos-cc {
position: absolute;
top: 45%;
left: 45%;
}
.d-none {
display: none !important;
}
</style>
<script src="../node_modules/jquery/dist/jquery.min.js"></script>
<script src="../node_modules/popper.js/dist/umd/popper.min.js"></script>
<script src="../node_modules/bootstrap/dist/js/bootstrap.min.js"></script>
<script src="../node_modules/lodash/lodash.min.js"></script>
<script src="../node_modules/moment/min/moment.min.js"></script>
<script src="../node_modules/numeral/min/numeral.min.js"></script>
<script src="../dist/formhandler.min.js"></script>
<script src="../node_modules/d3/build/d3.js"></script>
<script src="../node_modules/d3-scale-chromatic/dist/d3-scale-chromatic.js"></script>
<script src="tape.js"></script>
</head>
<body>
<script>
tape.onFinish(function () { window.renderComplete = true })
</script>
<div class="row_template1" data-table="grid" data-src="/formhandler-data"></div>
<script>
tape('$().formhandler() renders rowTemplate as string', function (t) {
$('.row_template1').formhandler({
columns: [
{
name: 'ID',
format: function(row) {return row['ID'].toLowerCase() }, // TODO: REVIEW whether to pass row number as second argument?
link: 'https://en.wikipedia.org/wiki/<%= ID %>',
template: function (value, formatted_value, link, data) {
return "<td>ID " + formatted_value + "</td>"
}
},
{
name: 'c1',
template: function (value, formatted_value, link, data) {
var f = d3.scaleLinear()
.domain([0, 100])
.range(['white', 'green'])
return '<td>' + value + '<svg height="20" width="20"><circle r="10" cx="10" cy="10" stroke="black" stroke-width="3" fill="'+ f(value) +'"></circle></svg></td>'
}
},
{
name: 'c2',
format: function (row) {
var f = d3.scaleLinear()
.domain([0, 100])
.range(['white', 'green'])
return row['c2'] + '<svg height="20" width="20"><circle r="10" cx="10" cy="10" stroke="black" stroke-width="3" fill="' + f(row['c2']) + '"></circle></svg>'
}
},
{ name: 'Continent' }
],
rowTemplate: function(row) {
var template = `<div class="col-sm-3 formhandler-grid-cell d-inline-block p-3 box-shadow">
<div class="thumbnail">
<img class="img img-responsive" src="/default.png"/>
<div class="caption">
<div>
<strong>ID</strong>:`+row['ID']+`
</div>
<div>
<strong>c1</strong>:`+row['c1']+`
</div>
<div>
<strong>c2</strong>:`+row['c2']+`
</div>
<div>
<strong>Continent</strong>:`+row['Continent']+`
</div>
</div>
</div>
</div>`
return template
},
pageSize: 3
}).on('load', function () {
var template = `<div class="col-sm-3 formhandler-grid-cell d-inline-block p-3 box-shadow">
<div class="thumbnail">
<img class="img img-responsive" src="/default.png">
<div class="caption">
<div>
<strong>ID</strong>:AND
</div>
<div>
<strong>c1</strong>:35
</div>
<div>
<strong>c2</strong>:1
</div>
<div>
<strong>Continent</strong>:Europe
</div>
</div>
</div>
</div> <div class="col-sm-3 formhandler-grid-cell d-inline-block p-3 box-shadow">
<div class="thumbnail">
<img class="img img-responsive" src="/default.png">
<div class="caption">
<div>
<strong>ID</strong>:ARE
</div>
<div>
<strong>c1</strong>:24
</div>
<div>
<strong>c2</strong>:0
</div>
<div>
<strong>Continent</strong>:Asia
</div>
</div>
</div>
</div> <div class="col-sm-3 formhandler-grid-cell d-inline-block p-3 box-shadow">
<div class="thumbnail">
<img class="img img-responsive" src="/default.png">
<div class="caption">
<div>
<strong>ID</strong>:AFG
</div>
<div>
<strong>c1</strong>:28
</div>
<div>
<strong>c2</strong>:1
</div>
<div>
<strong>Continent</strong>:Asia
</div>
</div>
</div>
</div>`
t.equal($('.row_template1 .table_grid')[0].innerHTML.trim(), template)
t.end()
})
})
</script>
</body>
</html>
This diff is collapsed.
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