Commit 33e015bd authored by S Anand's avatar S Anand

DOC: split documentation into separate files

parent 68e488f6
Pipeline #71665 passed with stage
in 2 minutes and 22 seconds
# Set up
To set up locally, clone this repo and run:
yarn install
npm run build # Optional: build the dist/ directory
Here is a list of npm commands available:
npm test # Run all unit tests
npm run lint # Check for basic errors using eslint
npm run server # Start a HTTP server at the current folder for manual testing
npm run dev # Start a rollup watch that re-builds dist/ if files change
# File structure # File structure
- `dist/` has output files. It is re-created via `npm run build`. It has: - `dist/` has output files. It is re-created via `npm run build`. It has:
...@@ -18,19 +32,30 @@ ...@@ -18,19 +32,30 @@
- `tape.js` is dynamically created using browserify to help with test cases. This is not committed - `tape.js` is dynamically created using browserify to help with test cases. This is not committed
- Other test dependencies - Other test dependencies
# Set up # Interaction conventions
To set up locally, clone this repo and run:
yarn install All interaction components use this naming convention:
npm run build # Optional: build the dist/ directory
Here is a list of npm commands available: - Interactions are enabled on a *container*, typically `body`. For example,
`$('body').urlfilter()`. Containers have these common attributes:
- `data-selector`: selector to identify triggers. e.g. `.urlfilter`, `.highlight`
- `data-target`: selector that all triggers act on by default
- `data-mode`: mode of interaction for all triggers
- `data-attr`: attribute that contains the interaction data, e.g. `href` for `.urlfilter`
- `data-event`: event that triggers urlfilter. Defaults to `'click'`
- Interactions are triggered on a *trigger*. For example, `.urlfilter` for `$().urlfilter()`.
Clicking / hovering on / typing in a trigger triggers the interaction.
- `data-target`: selector that this trigger acts on
- `data-mode`: mode of interaction for this trigger
- Interactions change a *target*. For example, `urlfilter` changes `location.href` by default. The
`data-target` on containers and triggers define this.
- Interactions data is contained in an attribute. This is applied to the target. For example,
`.urlfilter` applied `href` to the target. The attribute name is stored in `data-attr`.
- Interactions have *modes*. This can be controlled using `data-mode=`.
npm test # Run all unit tests All container `data-` attributes can also be passed as an option to the
npm run lint # Check for basic errors using eslint function. For example, `<body data-selector=".link">` is the same as
npm run server # Start a HTTP server at the current folder for manual testing `$('body').urlfilter({selector: '.link'})`.
npm run dev # Start a rollup watch that re-builds dist/ if files change
# Release # Release
......
This diff is collapsed.
# $.ajaxchain
Chains AJAX requests. [$.ajax][ajax] fetches a single page, like this:
```js
$.ajax({
url: 'formhandler', // Fetch "formhandler" URL
data: {_offset: 0} // with ?_offset=0
})
```
`$.ajaxchain` keeps fetching more page using a `chain:` function.
```js
var ajaxchain_instance = $.ajaxchain({
url: 'formhandler', // Fetch "formhandler" URL
data: {_offset: 0, _limit: 10}, // with ?_offset=0&_limit=10
chain: function(response, request) { // When the response is retrieved
if (response.length > 0) // if the response is non-empty
return {data: {_offset: request.data._offset + 10} // fetch the next page
},
limit: 10, // Get at most 10 pages (default)
// any other $.ajax options can be passed
})
```
The flow when `$.ajaxchain(request)` is called is:
1. Fetch URL using [$.ajax][ajax] using the request options
2. If the page limit is not reached, call the `.chain(response, request, xhr)` function.
- `request` is the request sent to [$.ajax][ajax]
- `response` is the response from [$.ajax][ajax]
- `xhr` is the [jqXHR][jqxhr] object
3. If `.chain()` returns a non-empty object
- update the `request` with the response of `.chain()`
- call `$.ajaxchain(request)` with the new request
- Otherwise, stop.
## $.ajaxchain options
`$.ajaxchain()` accepts the same options as [$.ajax][ajax] with a few additional options:
- `chain`: a function that returns updates to the request object
- `limit`: the maximum number of pages to fetch
The `chain:` function can be used with [FormHandler](https://learn.gramener.com/guide/formhandler/).
```js
chain: function(response, request) {
if (response.length > 0) // If the response is not empty
return {data: {_offset: request.data._offset + 10}} // Fetch the next page
}
```
You can use a set of pre-defined helper functions for chaining:
- `chain: $.ajaxchain.list()` chains a list of URLs.
- `chain: $.ajaxchain.list([url1, url2, url3])` fetches `url1`, `url2` and `url3` one after another
- `chain: $.ajaxchain.cursor(target, source)` uses a page cursor or token to identify the next page
- Requires [lodash](https://lodash.com/)
- The `source` is the [object path](https://lodash.com/docs/#get) from the
response that has the next cursor value. For example: `a.b` means `response.a.b`
- The `target` is the update to be made to the `request`. For example,
`data.token` sets `?token=`. `headers.X-Token` sets the `X-Token` header.
- Google APIs like [YouTube PlaylistItems](https://developers.google.com/youtube/v3/docs/playlistItems/list)
can use `chain: $.ajaxchain.cursor('data.pageToken', 'nextPageToken')`.
It fetches the next URL with `?pageToken=` as the `nextPageToken` key from the response.
- [Twitter APIs](https://developer.twitter.com/en/docs/ads/general/guides/pagination.html)
can use `chain: $.ajaxchain.cursor('data.cursor', 'next_cursor')`
- [Facebook APIs](https://developers.facebook.com/docs/graph-api/using-graph-api/#paging)
can use `chain: $.ajaxchain.cursor('url', 'paging.next')`
You can also construct your own `chain:` functions. For example:
On [YouTube](https://developers.google.com/youtube/v3/docs/playlistItems/list):
```js
chain: function(response, request) {
if (response.nextPageToken)
return {data: {pageToken: response.textPageToken}}
}
```
If the results are at `/page/1`, `/page/2`, etc:
```js
url: `/page/1`,
chain: function(response, request) {
if (response.length > 0)
return {url: `/page/` + (+request.url.split('/')[-1] + 1)}
}
```
## $.ajaxchain events
To access the response, we can use the `ajaxchain_instance` events. There are 3 events:
- `done` is fired when ALL pages have been loaded. Event attributes are:
- `response`: list of responses returned by each [$.ajax][ajax] request
- `request`: list of requests passed to each [$.ajax][ajax]
- `load` is fired when each page is loaded. Event attributes are:
- `request`: the parameters passed to [$.ajax][ajax] request
- `response`: data returned by [$.ajax][ajax] request
- `xhr`: the [jqXHR][jqxhr] object
- `error` is fired when there is an error. Event attributes are:
- `request`: the parameters passed to [$.ajax][ajax] request that failed
- `xhr`: the [jqXHR][jqxhr] object that failed
- `exception`: any exception thrown when calling the `chain` function
For example:
```js
ajaxchain_instance
.on('done', function(e) { // Called after ALL pages are loaded, or on error
// Responses are in e.response[0], e.response[1], etc
// Requests are in e.request[0], e.request[1], etc
})
.on('load', function(e) { // Called when each page is loaded
// e.response has the response
// e.request has the request
})
.on('error', function(e) { // Called when there's an error
// e.request has the request
// e.exception is set if the .change() function threw an exception
})
```
[ajax]: http://api.jquery.com/jQuery.ajax/
[jqxhr]: http://api.jquery.com/Types/#jqXHR
# g1.datafilter
`g1.datafilter(data, filters)` returns the filtered data based on the filters. While urlfilter on [$.formhandler](#formhandler) applies filtering on data server side, `datafilter` applies urlfilter on frontend loaded data.
For example:
```js
var data = [
{"ID": "1", "product": "Fan", "sales": "100", "city": "NY"},
{"ID": "2", "product": "Fan", "sales": "80", "city": "London"},
{"ID": "3", "product": "Fan", "sales": "120", "city": "NJ"},
{"ID": "4", "product": "Fan", "sales": "130", "city": "London"},
{"ID": "5", "product": "Light", "sales": "500", "city": "NY"},
{"ID": "5", "product": "Light", "sales": "100", "city": "London"}
]
g1.datafilter(data, {
'sales>': ['100'],
'city': ['London', 'NJ'],
'product': ['Fan']
})
// Returns [{"ID": "3", "product": "Fan", "sales": "120", "city": "NJ"}, {"ID": "4", "product": "Fan", "sales": "130", "city": "London"}]
```
g1.datafilter with multiple datasets:
```js
var data1 = [
{"ID": "1", "product": "Fan", "sales": "100", "city": "NY"},
{"ID": "2", "product": "Fan", "sales": "80", "city": "London"},
{"ID": "3", "product": "Fan", "sales": "120", "city": "NJ"},
{"ID": "4", "product": "Fan", "sales": "130", "city": "London"},
{"ID": "5", "product": "Light", "sales": "500", "city": "NY"},
{"ID": "5", "product": "Light", "sales": "100", "city": "London"}
]
var data2 = [
{"ID": "1", "city": "NY"},
{"ID": "2", "city": "London"},
{"ID": "3", "city": "NJ"},
{"ID": "4", "city": "London"},
{"ID": "5", "city": "NY"},
{"ID": "5", "city": "London"}
]
g1.datafilter(data1, {
'datsetname2:city': ['London', 'NJ'],
'sales>~': [100],
'datsetname1:product': ['Fan']
}, 'datsetname1'))
// ignores datsetname2:city: ['London', 'NJ']
// Returns [{"ID": "3", "product": "Fan", "sales": "120", "city": "NJ"}, {"ID": "4", "product": "Fan", "sales": "130", "city": "London"}, {"ID": "1", "product": "Fan", "sales": "100", "city": "NY"}]
g1.datafilter(data2, {
'datsetname2:city': ['London', 'NJ'],
'sales>~': [100],
'datsetname1:product': ['Fan']
}, 'datsetname2'))
// ignores datsetname1:product: ['Fan']
// Return [
// {"ID": "2", "city": "London"},
// {"ID": "3", "city": "NJ"},
// {"ID": "4", "city": "London"},
// {"ID": "5", "city": "London"}
// ]
```
- `data`: a list of objects
- `filters`: [formhandler filters][formhandler-filters] extracted using
`g1.url.parse(url).searchList`. This converts `?city=London&sales>=1000` to
this filters object: `{'city': ['London'], 'sales>': ['1000']}`
- `namespace`: (optional) If `namespace` is not given, all filters are applied
on the dataset. If `namespace` is given, only filters that begin with
`<namespace>:` or that have no `:` are applied
[formhandler-filters]: https://learn.gramener.com/guide/formhandler/#formhandler-filters
# $.dispatch
Triggers a native JavaScript event. For example:
```js
$('a.action').dispatch('click')
```
sends a click to `a.action`. Like [$.trigger](https://api.jquery.com/trigger/),
but this will fire non-jQuery event handlers as well.
## $.dispatch options
You can add an optional dict as the second parameter. It can have any
[event properties](https://developer.mozilla.org/en-US/docs/Web/API/Event#Properties)
as attributes. For example:
```js
$('a.action').dispatch('click', {bubbles: true, cancelable: false})
```
- bubbles: whether the event bubbles or not. default: true
- cancelable: whether the event is cancelable or not. default: true
- All other `new Event()` options will also work
https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events
# $.dropdown
`$.dropdown()` creates dropdowns that integrate well with
[$.urlfilter](#urlfilter) and [$.urlchange](#urlchange).
It requires the [bootstrap-select](https://silviomoreto.github.io/bootstrap-select/examples/)
library and its dependencies.
Example:
```html
<div class="container1"></div>
<script>
$('.container1').dropdown({data: ['Red', 'Green', 'Blue'] })
</script>
```
This renders a dropdown with 3 options -- Red, Green, Blue.
```html
<div class="container2"></div>
<script>
$('.container2').dropdown({ key: 'colors', data: ['Red', 'Green', 'Blue'] })
</script>
```
`key` enables urlfilter for dropdown. If Red option is selected from dropdown, URL is appended with `?colors=Red`
By default, the selected dropdown values are appended to URL query string. To append to the hash instead, use `target: '#'`.
```html
<div class="container3"></div>
<script>
$('.container3').dropdown(
{ key: 'colors',
data: ['Red', 'Green', 'Blue'],
target: '#'
})
</script>
```
To change URL without reloading the page, use `target: 'pushState'`.
```html
<div class="container4"></div>
<script>
$('.container4').dropdown(
{ key: 'colors',
data: ['Red', 'Green', 'Blue'],
target: 'pushState'
})
</script>
```
To use bootstrap-select options, use `options:`
```html
<div class="container5"></div>
<script>
$('.container5').dropdown({
data: ['Red', 'Green', 'Blue'], key: 'colors',
options: {
style: 'btn-primary',
liveSearch: true
}
})
</script>
```
## $.dropdown options
- `data`: Array of values.
- `url`: End point that returns the `data`. If `data:` is also given, `data` takes priority.
- `target`: defines how URL is updated. Can be `''` (Default), `#` or `pushState`
- `key`: key with which URL is updated.
- `multiple`: To render a dropdown that supports multi-select. Can be `true` or `false` (Default).
- `options`: Supports same options as [bootstrap-select options](https://silviomoreto.github.io/bootstrap-select/options/)
## $.dropdown events
- `load` is triggered after dropdown is rendered
- `change` is triggered whenever dropdown value is changed
```html
<div class="container5"></div>
<script>
$('.container5')
.on('load', function() {
// Your code here
})
.on('change', function() {
// Your code here
})
.dropdown({
key: 'colors',
data: ['Red', 'Green', 'Blue']
})
</script>
```
# $.formhandler
An interactive table component for [FormHandler][formhandler] data.
```html
<div class="formhandler" data-src="formhandler-url" data-page-size="10"></div>
<script>
$('.formhandler').formhandler({
pageSize: 20
})
</script>
```
Options can passed via an options dict, and over-ridden using `data-` attributes.
In the above example, `data-page-size="10"` over-rides `pageSize: 20`.
[formhandler]: https://learn.gramener.com/guide/formhandler/
## $.formhandler options
The full list of options is below. Simple options can be specified as `data-` attributes as well.
- `src`: [FormHandler][formhandler] URL endpoint
- `data`: Array of objects. Dataset for formhandler table. If both `src` and `data` are provided, `data` takes priority.
- `namespace`: (Optional) If the URL has `?name:key=value`, the filter
`key=value` only applies to formhandlers with namespace as `name`.
Filters without a namespace like `?key=value` will apply to all formhandlers.
- `columns`: comma-separated column names to display, or a list of objects with these keys:
- `name`: column name. `"*"` is a special column placeholder for "all columns" (options given for `"*"` are applied 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 returns formatted cell display value.
- function accepts an object with these keys:
- `name`: column name
- `value`: cell data value
- `row`: row data
- `index`: row index
- `data`: the dataset from `src`
- 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.
- `template`: string template / function that renders the cell.
- function accepts an object with these keys:
- `name`: column name
- `value`: cell data value
- `format`: formatted cell display value
- `link`: cell link value (if applicable)
- `index`: row index
- `row`: row data
- `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:
- `{'', 'Equals...', '!', 'Does not equal...', ...}`.
The default list of operators is based on the auto-detected type of the column.
- `link`: string / function that generates a link for this each cell.
- If no `link:` is specified, clicking on the cell filters by that cell.
- If `link:` is a function, opens a new window with the URL as `fn(args)`.
- function accepts an object with these keys:
- `name`: column name
- `value`: cell data value
- `format`: formatted cell display value
- `index`: row index
- `row`: row data
- `data`: the dataset from `src`
Example: `function(args) { return 'https://example.org/city/' + args.value }`
- If `link:` is a string, opens a new window with the string URL interpolated as a lodash template with an object (mentioned above) as data.
Example: `"https://example.org/city/<%- value >"`
- `hideable`: `true` (default) / `false`. Show or hide `Hide` option in header dropdown
- `hide`: `true` / `false` (default). Hides the column
- `unique`: TODO: {dict of query parameter and display value} or [list of values] or function?
- `edit`: Shows the Edit control. Can be `true` / `false` (default). Can also pass an object.
- `done`: function that gets called after saving the edited row.
- `add`: Show the Add control. Can be `true` / `false` (default). Can also pass an object.
- `done`: function that gets called after saving the new row.
- `actions`: A list of objects. you need not add it to actions
- `{{action}}`: a function() that gets triggered on clicking the element with `data-action='{{action}}` attribute. The value of `data-action` attribute must match with key `{{action}}` in `actions`.
- function accepts an object with these keys:
- `row`: row data
- `index`: index of the row in the dataset from `src`
Example:
- `highlight_row`: `function(obj) { $(obj.row).addClass('.yellow_color')}`. Either a new column can be defined in `columns:` (example: {`name`: `Additional Col`}) with cell_template having an element with data attribute as `data-action='highlight_row'` or can use an existing column but with custom template that has an element with data attribute as `data-action='highlight_row'`.
- Note: DELETE operation is executed on a row if an element has data attribute `data-action='delete'`. If `delete` action is given in `actions`, the function given for `delete` is executed on click of an element with `data-action='delete'` instead od executing DELETE operation.
- `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
- `size`: Shows the page size control. Can be `true` (default) / `false`
- `sizeValues`: Allowed page size values. Defaults to `[10, 20, 50, 100, 500, 1000]`
- `export`: Shows the export control. Can be `true` (default) / `false`
- `exportFormats`: {xlsx: 'Excel'}
- `filters`: Shows the applied filters control. Can be `true` (default) / `false`
- `transform`: an optional function() that modifies data. It accepts a dict that has keys:
- `data`: the FormHandler data
- `meta`: the FormHandler metadata from the `FH-*` HTTP headers
- `args`: the URL query parameters passed to the FormHandler
- `options`: the options applicable to the FormHandler
- returns a dict with modified values of `data` and `meta`
- `icon`: if `table: 'grid'` is used, display an icon. string / function that renders the cell.
- function accepts an object with these keys:
- `row`: row data
- `data`: the dataset from `src`
- `index`: index of the row in the dataset from `src`
Example:
- `icon: 'fa fa-home fa-3x'` renders a FontAwesome home icon
- `icon: './path/to/image.png'` renders the image specified
- `icon: function(args) { return args.row['image_link'] }` renders an image with `src` attribute as the value from column name `image_link`
**Advanced**. Each component can have a target which specifies a selector. For
e.g., to render the export button somewhere else, use
`data-export-target=".navbar-export"`. This replaces the `.navbar-export`
contents with the export button. (It searches within the table container for
`.navbar-export` first, and if not found, searches everywhere.) Available
targets are:
- `tableTarget`
- `countTarget`
- `pageTarget`
- `sizeTarget`
- `exportTarget`
- `filtersTarget`
- `searchTarget`
**Advanced**: Each component's template string can be over-ridden. For example,
`data-search-template="<input type='search'>"` will replace the search template
with a simple input. Available template strings are:
- `tableTemplate`
- `countTemplate`
- `pageTemplate`
- `sizeTemplate`
- `exportTemplate`
- `filtersTemplate`
- `searchTemplate`
- `rowTemplate`, which can be a string template or function
- function accepts an object with these keys:
- `row`: row data
- `index`: row index
- `data`: the dataset from `src`
- string template can use the above variables
Features to be implemented:
- Loading indicator
- Full text search
- URL targets other than '#', e.g. pushState
## $.formhandler events
- `load` is fired on the source when the template is rendered. Attributes:
- `formdata`: the FormHandler data
- `meta`: the FormHandler metadata
- `args`: the URL query parameters passed to the request
- `options`: applied options to the FormHandler
Note: Make sure `load` event listener is attached before calling `$.formhandler()`
## $.formhandler examples
Render a table using the FormHandler at `./data`:
```html
<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>
```
# $.highlight
`$.highlight()` highlights elements when hovering on or clicking another element.
Example:
```html
<div data-toggle="highlight" data-target="a.red" data-classes="active">Link</a>
<div data-toggle="highlight" data-target="a.red" data-classes="active" data-mode="click">Link</a>
<script>
$('body').highlight({
classes: 'shadow',
mode: 'click'
})
</script>
```
## $.highlight attributes
When we run `$('body').highlight()`, the `body` is called a "container". It
listens to events on "triggers", like `<... data-toggle="highlight">`
Highlight triggers support these attributes:
- `data-toggle="highlight"` indicates that the element acts as a highlighter
- `data-target=` selectors to highlight (required)
- `data-mode="click"` highlights on click. Use `data-mode="hover"` to higlight on hover (default)
- `data-classes=` space-separated class names to toggle on target elements
Highlight containers support these attributes:
- `data-selector=` defines the triggers, i.e. which nodes $.highlight applies to. Default: `[data-toggle="highlight"]`
- `data-mode` is the same as specifying data-mode on every trigger. Default: `hover`
- `data-attr` is the attribute that defines classes to toggle. Default: `data-classes`
- `data-classes=` is the same as specifiying the data-classes on every trigger. Default: `active`
## $.highlight events
- `highlight` is fired on the trigger when activated. Attributes:
- `target`: elements that match the `data-target=` selector
# g1.mapviewer
Mapviewer is an abstraction over [Leaflet](http://leafletjs.com/) that can
create common GIS applications using configurations.
Mapviewer requires `npm install leaflet d3 d3-scale-chromatic g1`.
```html
<link rel="stylesheet" href="node_modules/leaflet/dist/leaflet.css">
<script src="node_modules/leaflet/dist/leaflet.js"></script>
<script src="node_modules/d3/build/d3.js"></script>
<script src="node_modules/d3-scale-chromatic/dist/d3-scale-chromatic.min.js"></script>
<script src="node_modules/g1/dist/mapviewer.min.js"></script>
```