You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@zeppelin.apache.org by mo...@apache.org on 2017/04/14 22:34:37 UTC
[3/3] zeppelin git commit: [ZEPPELIN-2217] AdvancedTransformation for
Visualization
[ZEPPELIN-2217] AdvancedTransformation for Visualization
### What is this PR for?
`AdvancedTransformation` has more detailed options while providing existing features of `PivotTransformation` and `ColumnselectorTransformation` which Zeppelin already has
![av_in_30sec](https://cloud.githubusercontent.com/assets/4968473/24037330/c9478e86-0b40-11e7-9886-1ffb85042a7a.gif)
Here are some features which advanced-transformation can provide.
1. **(screenshot)** multiple sub charts
2. **(screenshot)** parameter widgets: `input`, `checkbox`, `option`, `textarea`
3. **(screenshot)** expand/fold axis and parameter panels
4. **(screenshot)** clear axis and parameter panels
5. **(screenshot)** remove duplicated columns in an axis
6. **(screenshot)** limit column count in an axis
7. configurable char axes: `valueType`, `axisType`, `description`, ...
8. configurable chart parameters
9. lazy transformation
10. parsing parameters automatically based on their type: `int`, `float`, `string`, `JSON`
11. multiple transformation methods
12. re-initialize whole configuration based on spec hash.
13. **(screenshot)** shared axis
#### API Details: Spec
`AdvancedTransformation` requires `spec` which includes axis and parameter details for charts.
- Let's create 2 sub-charts called `simple-line` and `step-line`.
- Each sub chart can have different `axis` and `parameter` depending on their requirements.
```js
constructor(targetEl, config) {
super(targetEl, config)
const spec = {
charts: {
'simple-line': {
sharedAxis: true, /** set if you want to share axes between sub charts, default is `false` */
axis: {
'xAxis': { dimension: 'multiple', axisType: 'key', },
'yAxis': { dimension: 'multiple', axisType: 'aggregator'},
'category': { dimension: 'multiple', axisType: 'group', },
},
parameter: {
'xAxisUnit': { valueType: 'string', defaultValue: '', description: 'unit of xAxis', },
'yAxisUnit': { valueType: 'string', defaultValue: '', description: 'unit of yAxis', },
'dashLength': { valueType: 'int', defaultValue: 0, description: 'the length of dash', },
},
},
'step-line': {
axis: {
'xAxis': { dimension: 'single', axisType: 'unique', },
'yAxis': { dimension: 'multiple', axisType: 'value', },
},
parameter: {
'xAxisUnit': { valueType: 'string', defaultValue: '', description: 'unit of xAxis', },
'yAxisUnit': { valueType: 'string', defaultValue: '', description: 'unit of yAxis', },
'noStepRisers': { valueType: 'boolean', defaultValue: false, description: 'no risers in step line', widget: 'checkbox', },
},
},
}
this.transformation = new AdvancedTransformation(config, spec)
}
```
#### API Details: Axis Spec
| Field Name | Available Values (type) | Description |
| --- | --- | --- |
|`dimension` | `single` | Axis can contains only 1 column |
|`dimension` | `multiple` | Axis can contains multiple columns |
|`axisType` | `key` | Column(s) in this axis will be used as `key` like in `PivotTransformation`. These columns will be served in `column.key` |
|`axisType` | `aggregator` | Column(s) in this axis will be used as `value` like in `PivotTransformation`. These columns will be served in `column.aggregator` |
|`axisType` | `group` | Column(s) in this axis will be used as `group` like in `PivotTransformation`. These columns will be served in `column.group` |
|`axisType` | (string) | Any string value can be used here. These columns will be served in `column.custom` |
|`maxAxisCount` | (int) | The maximum column count that this axis can contains. (unlimited if `undefined`) |
|`valueType` | (string) | Describe the value type just for annotation |
Here is an example.
```js
axis: {
'xAxis': { dimension: 'multiple', axisType: 'key', },
'yAxis': { dimension: 'multiple', axisType: 'aggregator'},
'category': { dimension: 'multiple', axisType: 'group', maxAxisCount: 2, valueType: 'string', },
},
```
#### API Details: Parameter Spec
| Field Name | Available Values (type) | Description |
| --- | --- | --- |
|`valueType` | `string` | Parameter which has string value |
|`valueType` | `int` | Parameter which has int value |
|`valueType` | `float` | Parameter which has float value |
|`valueType` | `boolean` | Parameter which has boolean value used with `checkbox` widget usually |
|`valueType` | `JSON` | Parameter which has JSON value used with `textarea` widget usually. `defaultValue` should be `""` (empty string). This ||`defaultValue` | (any) | Default value of this parameter. `JSON` type should have `""` (empty string) |
|`description` | (string) | Description of this parameter. This value will be parsed as HTML for pretty output |
|`widget` | `input` | Use [input](https://developer.mozilla.org/en/docs/Web/HTML/Element/input) widget. This is the default widget (if `widget` is undefined)|
|`widget` | `checkbox` | Use [checkbox](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/checkbox) widget. |
|`widget` | `textarea` | Use [textarea](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea) widget. |
|`widget` | `option` | Use [select + option](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select) widget. This parameter should have `optionValues` field as well. |
|`optionValues` | (Array<string>) | Available option values used with the `option` widget |
Here is an example.
```js
parameter: {
// string type, input widget
'xAxisUnit': { valueType: 'string', defaultValue: '', description: 'unit of xAxis', },
// boolean type, checkbox widget
'inverted': { widget: 'checkbox', valueType: 'boolean', defaultValue: false, description: 'invert x and y axes', },
// string type, option widget with `optionValues`
'graphType': { widget: 'option', valueType: 'string', defaultValue: 'line', description: 'graph type', optionValues: [ 'line', 'smoothedLine', 'step', ], },
// HTML in `description`
'dateFormat': { valueType: 'string', defaultValue: '', description: 'format of date (<a href="https://docs.amcharts.com/3/javascriptcharts/AmGraph#dateFormat">doc</a>) (e.g YYYY-MM-DD)', },
// JSON type, textarea widget
'yAxisGuides': { widget: 'textarea', valueType: 'JSON', defaultValue: '', description: 'guides of yAxis ', },
```
#### API Details: Transformer Spec
`AdvancedTransformation` supports 3 transformation methods. The return value will depend on the transformation method type.
```js
const spec = {
charts: {
'simple': {
/** default value of `transform.method` is the flatten cube. */
axis: { ... },
parameter: { ... }
},
'cube-group': {
transform: { method: 'cube', },
axis: { ... },
parameter: { ... },
}
'no-group': {
transform: { method: 'raw', },
axis: { ... },
parameter: { ... },
}
```
| Field Name | Available Values (type) | Description |
| --- | --- | --- |
|`method` | `object` | designed for [amcharts: serial](https://www.amcharts.com/demos/date-based-data/) |
|`method` | `array` | designed for [highcharts: column](http://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/demo/column-basic/) |
|`method` | `drill-down` | designed for [highcharts: drill-down](http://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/demo/column-drilldown/) |
|`method` | `raw` | will return the original `tableData.rows` |
Whatever you specified as `transform.method`, the `transformer` value will be always function for lazy computation.
```js
// advanced-transformation.util#getTransformer
if (transformSpec.method === 'raw') {
transformer = () => { return rows; }
} else if (transformSpec.method === 'array') {
transformer = () => {
...
return { ... }
}
}
```
#### Feature Details: Automatic parameter parsing
Advanced transformation will parse parameter values automatically based on their type: `int`, `float`, `string`, `JSON`
- See also `advanced-transformation-util.js#parseParameter`
#### Feature Details: re-initialize the whole configuration based on spec hash
```js
// advanced-transformation-util#initializeConfig
const currentVersion = JSON.stringify(spec)
if (!config.spec || !config.spec.version || config.spec.version !== currentVersion) {
spec.version = currentVersion
// reset config...
}
```
#### Feature Details: Shared Axes
If you set `sharedAxis` to `true` in chart specification, then these charts will share their axes. (default is `false`)
```js
const spec = {
charts: {
'column': {
transform: { method: 'array', },
sharedAxis: true,
axis: { ... },
parameter: { ... },
},
'stacked': {
transform: { method: 'array', },
sharedAxis: true,
axis: { ... }
parameter: { ... },
},
```
![sharedaxis](https://cloud.githubusercontent.com/assets/4968473/24207116/6999ad8a-0f63-11e7-8b61-273b712612fc.gif)
#### API Details: Usage in Visualization#render()
Let's assume that we want to create 2 sub-charts called `basic` and `no-group`.
- https://github.com/1ambda/zeppelin-ultimate-line-chart (an practical example)
```js
drawBasicChart(parameter, column, transformer) {
const { ... } = transformer()
}
drawNoGroupChart(parameter, column, transformer) {
const { ... } = transformer()
}
render(data) {
const { chart, parameter, column, transformer, } = data
if (chart === 'basic') {
this.drawBasicChart(parameter, column, transformer)
} else if (chart === 'no-group') {
this.drawNoGroupChart(parameter, column, transformer)
}
}
```
### What type of PR is it?
[Feature]
### Todos
NONE
### What is the Jira issue?
[ZEPPELIN-2217](https://issues.apache.org/jira/browse/ZEPPELIN-2217)
### How should this be tested?
1. Clone https://github.com/1ambda/zeppelin-ultimate-line-chart
2. Create a symbolic link `ultimate-line-chart.json` into `$ZEPPELIN_HOME/helium`
3. Modify the `artifact` value to proper absolute path considering your local machine.
4. Install the above visualization in `localhost:9000/#helium`
5. Test it
### Screenshots (if appropriate)
#### 1. *(screenshot)* multiple sub charts
![av_multiple_charts](https://cloud.githubusercontent.com/assets/4968473/24034638/7b84dba0-0b35-11e7-989d-059ccc87f968.gif)
#### 2. *(screenshot)* parameter widgets: `input`, `checkbox`, `option`, `textarea`
![av_widgets_new](https://cloud.githubusercontent.com/assets/4968473/24034652/88679d6c-0b35-11e7-835a-3970d7124850.gif)
#### 3. *(screenshot)* expand/fold axis and parameter panels
![av_fold_expand](https://cloud.githubusercontent.com/assets/4968473/24034653/8a634ddc-0b35-11e7-9851-15280a6b5fd3.gif)
#### 4. *(screenshot)* clear axis and parameter panels
![av_clean_buttons](https://cloud.githubusercontent.com/assets/4968473/24034654/8d3dc14a-0b35-11e7-98c7-3aeddce6d80a.gif)
#### 5. *(screenshot)* remove duplicated columns in an axis
![av_duplicated_columns](https://cloud.githubusercontent.com/assets/4968473/24034657/910f4d20-0b35-11e7-9e9b-d9e2f799a5dd.gif)
#### 6. *(screenshot)* limit column count in an axis
![av_maxaxiscount](https://cloud.githubusercontent.com/assets/4968473/24034679/a5e8eb34-0b35-11e7-89cd-070f3790d511.gif)
### Questions:
* Does the licenses files need update? - NO
* Is there breaking changes for older versions? - NO
* Does this needs documentation? - NO
Author: 1ambda <1a...@gmail.com>
Closes #2098 from 1ambda/ZEPPELIN-2217/advanced-transformation and squashes the following commits:
6cde7c9 [1ambda] fix reset params when spec change
c75a3f2 [1ambda] fix: Reset persisted axis
6a2130a [1ambda] fix: clear config only when axis changed
5464e84 [1ambda] fix: Optimize array 2 key method
9beb1e7 [1ambda] fix: Type error
2408225 [1ambda] test: Add test for array 2key
bf56761 [1ambda] feat: Add array:2-key transform method
7c6768f [1ambda] feat: Use axisSpec.desc as tooltip
f98d4c9 [1ambda] fix: Remove invalid key prop
5cf2ece [1ambda] feat: Add minAxisCount
4887800 [1ambda] fix: Remove local module yarn caches
3e29572 [1ambda] refactor: copyModule func
c91a033 [1ambda] fix: Set yarn cache dir in helium-bundles
04b5140 [1ambda] fix: Import a-tr
0a876cf [1ambda] docs: Update index.md
380b1af [1ambda] docs: Fix typo and add desc for existing trs
908214b [1ambda] docs: Move experimental tags
a009627 [1ambda] feat: Allow dup aggr axis
3b44e92 [1ambda] fix: Remove unuse const
ab6c22e [1ambda] test: Add test for drill-down method
756107a [1ambda] test: Add array transformation method
d819c73 [1ambda] test: Add object method
bf00fba [1ambda] test: Add MockTableData
39fe5ae [1ambda] test: Add test for getColumnsFromAxis
4c393b4 [1ambda] fix: Add polyfill for es6 funcs in test
e92c787 [1ambda] test: Add test for rmDup, aplMaxAxisCount
843f45d [1ambda] test: Add test for getCurrent* funcs
ae5277c [1ambda] test: Add test for initializeConfig
c14a9dc7 [1ambda] test: Add tests for widget, params
c510af1 [1ambda] docs: Add doc for Transformation
52db37b [1ambda] feat: Show panel menus only when opened
17ad4a4 [1ambda] feat: Support chartChanged, parameterChanged
c0d33d3 [1ambda] fix: Sort selectors in drilldown method
cfd6fef [1ambda] feat: sharedAxis
9af80ce [1ambda] style: Indent
79b5654 [1ambda] fix: return the same info in transform
7bee464 [1ambda] fix: Keynames
ee8788e [1ambda] feat: Support drill-down
666025a [1ambda] fix: DON'T reset current chart
ae1891f [1ambda] add array:key transform
4167a2e [1ambda] fix: Sort keyNames
912b5b7 [1ambda] fix: Persist initialized config
f1f6b0c [1ambda] feat: Support ARRAY transform.method
812f9a2 [1ambda] fix: Set proper aggr value when 0 group
20f9437 [1ambda] fix: getCube func
25d51a9 [1ambda] DON'T display aggr.name when aggrColumns.length == 1
f37e13d [1ambda] fix: Add 'object' transform.method
da2370c [1ambda] fix: Add resetAxis, Param funcs
2370682 [1ambda] fix: average is not caculated correctly
dd08e38 [1ambda] fix: Set param panel height to 400
881695a [1ambda] feat: clear chart, param separately
4d0d62b [1ambda] fix: DON'T clean panel config
92676d1 [1ambda] fix: limit parameter panel height to 370
cc29060 [1ambda] feat: parse param description as HTML
9a2d227 [1ambda] fix: Stop event propagation in widgets
fcc625c [1ambda] feat: Automatic param parsing
b4d774c [1ambda] fix: Dont close param panel when enter
088705b [1ambda] refactor: Remove util and add Widget funcs
bf88b4f [1ambda] feat: textare widget and update hook
4e73012 [1ambda] feat: widget checkbox
11b7eaa [1ambda] feat: option widget
5d3efc9 [1ambda] fix: Change panel header
b1d9d31 [1ambda] feat: Save and close with enter key
53f508c [1ambda] feat: custom axisSpec
0dbc431 [1ambda] feat: Support transformer
94d837a [1ambda] feat: Automatic spec versioning
74b8b4e [1ambda] fix: Duplicated radio btn id, name
5b88f08 [1ambda] fix: Modify margin of subchart radio btns
019892c [1ambda] feat: Support transform: flatten
0484e1e [1ambda] feat: Support maxAxisCount in axisSpec
936901b [1ambda] feat: Support undefined valueType in axisSpec
7a454ff [1ambda] feat: Cube Transformation
f0ed02f [1ambda] feat: Support same axis types
49985c6 [1ambda] refactor: Refine axis, param spec
d89e223 [1ambda] feat: advanced-transformation-api
75569ce [1ambda] feat: Support multiple charts in UI
e1fcc2e [1ambda] feat: Support multiple charts
97be629 [1ambda] fix: Add singleDimensionAggregatorChanged
676bd7e [1ambda] refactor: Refine transform API
9fb398e [1ambda] feat: Add clearConfig
a8a4fb1 [1ambda] refactor: Add getAxisInSingleDimension func
9768ecf [1ambda] feat: Add groupBase axis option
91ae54d [1ambda] fix: Overflow issue in single aggr
10c80fc [1ambda] feat: AdvancedTransformation
Project: http://git-wip-us.apache.org/repos/asf/zeppelin/repo
Commit: http://git-wip-us.apache.org/repos/asf/zeppelin/commit/45cc8a9e
Tree: http://git-wip-us.apache.org/repos/asf/zeppelin/tree/45cc8a9e
Diff: http://git-wip-us.apache.org/repos/asf/zeppelin/diff/45cc8a9e
Branch: refs/heads/master
Commit: 45cc8a9e8a23a57271dec384245d0012a0e5e608
Parents: 2173b40
Author: 1ambda <1a...@gmail.com>
Authored: Tue Apr 11 23:14:46 2017 +0900
Committer: Lee moon soo <mo...@apache.org>
Committed: Sat Apr 15 07:34:27 2017 +0900
----------------------------------------------------------------------
docs/_includes/themes/zeppelin/_navigation.html | 9 +-
docs/development/writingzeppelinapplication.md | 4 +-
docs/development/writingzeppelinspell.md | 4 +-
.../development/writingzeppelinvisualization.md | 4 +-
...itingzeppelinvisualization_transformation.md | 281 +++
docs/index.md | 9 +-
zeppelin-web/karma.conf.js | 6 +-
zeppelin-web/package.json | 2 +-
.../advanced-transformation-setting.html | 280 +++
.../tabledata/advanced-transformation-util.js | 1259 +++++++++++++
.../advanced-transformation-util.test.js | 1746 ++++++++++++++++++
.../app/tabledata/advanced-transformation.js | 230 +++
zeppelin-web/src/index.js | 1 +
.../zeppelin/helium/HeliumBundleFactory.java | 101 +-
.../helium/HeliumBundleFactoryTest.java | 2 +-
15 files changed, 3878 insertions(+), 60 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/zeppelin/blob/45cc8a9e/docs/_includes/themes/zeppelin/_navigation.html
----------------------------------------------------------------------
diff --git a/docs/_includes/themes/zeppelin/_navigation.html b/docs/_includes/themes/zeppelin/_navigation.html
index 623fac3..4e49a1a 100644
--- a/docs/_includes/themes/zeppelin/_navigation.html
+++ b/docs/_includes/themes/zeppelin/_navigation.html
@@ -117,10 +117,11 @@
<li><a href="{{BASE_PATH}}/security/notebook_authorization.html">Notebook Authorization</a></li>
<li><a href="{{BASE_PATH}}/security/datasource_authorization.html">Data Source Authorization</a></li>
<li role="separator" class="divider"></li>
- <li class="title"><span><b>Helium Framework</b><span></li>
- <li><a href="{{BASE_PATH}}/development/writingzeppelinapplication.html">Writing Zeppelin Application (Experimental)</a></li>
- <li><a href="{{BASE_PATH}}/development/writingzeppelinspell.html">Writing Zeppelin Spell (Experimental)</a></li>
- <li><a href="{{BASE_PATH}}/development/writingzeppelinvisualization.html">Writing Zeppelin Visualization (Experimental)</a></li>
+ <li class="title"><span><b>Helium Framework (Experimental)</b></span></li>
+ <li><a href="{{BASE_PATH}}/development/writingzeppelinapplication.html">Writing Zeppelin Application</a></li>
+ <li><a href="{{BASE_PATH}}/development/writingzeppelinspell.html">Writing Zeppelin Spell</a></li>
+ <li><a href="{{BASE_PATH}}/development/writingzeppelinvisualization.html">Writing Zeppelin Visualization: Basics</a></li>
+ <li><a href="{{BASE_PATH}}/development/writingzeppelinvisualization_transformation.html">Writing Zeppelin Visualization: Transformation</a></li>
<li role="separator" class="divider"></li>
<li class="title"><span><b>Advanced</b><span></li>
<li><a href="{{BASE_PATH}}/install/virtual_machine.html">Zeppelin on Vagrant VM</a></li>
http://git-wip-us.apache.org/repos/asf/zeppelin/blob/45cc8a9e/docs/development/writingzeppelinapplication.md
----------------------------------------------------------------------
diff --git a/docs/development/writingzeppelinapplication.md b/docs/development/writingzeppelinapplication.md
index 7048cb3..59f980a 100644
--- a/docs/development/writingzeppelinapplication.md
+++ b/docs/development/writingzeppelinapplication.md
@@ -1,6 +1,6 @@
---
layout: page
-title: "Writing a new Application(Experimental)"
+title: "Writing a new Application"
description: "Apache Zeppelin Application is a package that runs on Interpreter process and displays it's output inside of the notebook. Make your own Application in Apache Zeppelin is quite easy."
group: development
---
@@ -19,7 +19,7 @@ limitations under the License.
-->
{% include JB/setup %}
-# Writing a new Application (Experimental)
+# Writing a new Application
<div id="toc"></div>
http://git-wip-us.apache.org/repos/asf/zeppelin/blob/45cc8a9e/docs/development/writingzeppelinspell.md
----------------------------------------------------------------------
diff --git a/docs/development/writingzeppelinspell.md b/docs/development/writingzeppelinspell.md
index 02a2301..1eefe83 100644
--- a/docs/development/writingzeppelinspell.md
+++ b/docs/development/writingzeppelinspell.md
@@ -1,6 +1,6 @@
---
layout: page
-title: "Writing a new Spell(Experimental)"
+title: "Writing a new Spell"
description: "Spell is a kind of interpreter that runs on browser not on backend. So, technically it's the frontend interpreter. "
group: development
---
@@ -19,7 +19,7 @@ limitations under the License.
-->
{% include JB/setup %}
-# Writing a new Spell (Experimental)
+# Writing a new Spell
<div id="toc"></div>
http://git-wip-us.apache.org/repos/asf/zeppelin/blob/45cc8a9e/docs/development/writingzeppelinvisualization.md
----------------------------------------------------------------------
diff --git a/docs/development/writingzeppelinvisualization.md b/docs/development/writingzeppelinvisualization.md
index 5ccd970..5a0601f 100644
--- a/docs/development/writingzeppelinvisualization.md
+++ b/docs/development/writingzeppelinvisualization.md
@@ -1,6 +1,6 @@
---
layout: page
-title: "Writing a new Visualization(Experimental)"
+title: "Writing a new Visualization"
description: "Apache Zeppelin Visualization is a pluggable package that can be loaded/unloaded on runtime through Helium framework in Zeppelin. A Visualization is a javascript npm package and user can use them just like any other built-in visualization in a note."
group: development
---
@@ -19,7 +19,7 @@ limitations under the License.
-->
{% include JB/setup %}
-# Writing a new Visualization (Experimental)
+# Writing a new Visualization
<div id="toc"></div>
http://git-wip-us.apache.org/repos/asf/zeppelin/blob/45cc8a9e/docs/development/writingzeppelinvisualization_transformation.md
----------------------------------------------------------------------
diff --git a/docs/development/writingzeppelinvisualization_transformation.md b/docs/development/writingzeppelinvisualization_transformation.md
new file mode 100644
index 0000000..22bf130
--- /dev/null
+++ b/docs/development/writingzeppelinvisualization_transformation.md
@@ -0,0 +1,281 @@
+---
+layout: page
+title: "Transformations for Zeppelin Visualization"
+description: "Description for Transformations"
+group: development
+---
+<!--
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+{% include JB/setup %}
+
+# Transformations for Zeppelin Visualization
+
+<div id="toc"></div>
+
+## Overview
+
+Transformations
+
+- **renders** setting which allows users to set columns and
+- **transforms** table rows according to the configured columns.
+
+Zeppelin provides 4 types of transformations.
+
+## 1. PassthroughTransformation
+
+`PassthroughTransformation` is the simple transformation which does not convert original tabledata at all.
+
+See [passthrough.js](https://github.com/apache/zeppelin/blob/master/zeppelin-web/src/app/tabledata/passthrough.js)
+
+## 2. ColumnselectorTransformation
+
+`ColumnselectorTransformation` is uses when you need `N` axes but do not need aggregation.
+
+See [columnselector.js](https://github.com/apache/zeppelin/blob/master/zeppelin-web/src/app/tabledata/columnselector.js)
+
+## 3. PivotTransformation
+
+`PivotTransformation` provides group by and aggregation. Every chart using `PivotTransformation` has 3 axes. `Keys`, `Groups` and `Values`.
+
+See [pivot.js](https://github.com/apache/zeppelin/blob/master/zeppelin-web/src/app/tabledata/pivot.js)
+
+## 4. AdvancedTransformation
+
+`AdvancedTransformation` has more detailed options while providing existing features of `PivotTransformation` and `ColumnselectorTransformation`
+
+- multiple sub charts
+- configurable chart axes
+- parameter widgets: `input`, `checkbox`, `option`, `textarea`
+- parsing parameters automatically based on their types
+- expand / fold axis and parameter panels
+- multiple transformation methods while supporting lazy converting
+- re-initialize the whole configuration based on spec hash.
+
+### Spec
+
+`AdvancedTransformation` requires `spec` which includes axis and parameter details for charts.
+
+Let's create 2 sub-charts called `line` and `no-group`. Each sub chart can have different axis and parameter depending on their requirements.
+
+<br/>
+
+```js
+class AwesomeVisualization extends Visualization {
+ constructor(targetEl, config) {
+ super(targetEl, config)
+
+ const spec = {
+ charts: {
+ 'line': {
+ transform: { method: 'object', },
+ sharedAxis: false, /** set if you want to share axes between sub charts, default is `false` */
+ axis: {
+ 'xAxis': { dimension: 'multiple', axisType: 'key', description: 'serial', },
+ 'yAxis': { dimension: 'multiple', axisType: 'aggregator', description: 'serial', },
+ 'category': { dimension: 'multiple', axisType: 'group', description: 'categorical', },
+ },
+ parameter: {
+ 'xAxisUnit': { valueType: 'string', defaultValue: '', description: 'unit of xAxis', },
+ 'yAxisUnit': { valueType: 'string', defaultValue: '', description: 'unit of yAxis', },
+ 'lineWidth': { valueType: 'int', defaultValue: 0, description: 'width of line', },
+ },
+ },
+
+ 'no-group': {
+ transform: { method: 'object', },
+ sharedAxis: false,
+ axis: {
+ 'xAxis': { dimension: 'single', axisType: 'key', },
+ 'yAxis': { dimension: 'multiple', axisType: 'value', },
+ },
+ parameter: {
+ 'xAxisUnit': { valueType: 'string', defaultValue: '', description: 'unit of xAxis', },
+ 'yAxisUnit': { valueType: 'string', defaultValue: '', description: 'unit of yAxis', },
+ },
+ },
+ }
+
+ this.transformation = new AdvancedTransformation(config, spec)
+ }
+
+ ...
+
+ // `render` will be called whenever `axis` or `parameter` is changed
+ render(data) {
+ const { chart, parameter, column, transformer, } = data
+
+ if (chart === 'line') {
+ const transformed = transformer()
+ // draw line chart
+ } else if (chart === 'no-group') {
+ const transformed = transformer()
+ // draw no-group chart
+ }
+ }
+}
+```
+
+<br/>
+
+### Spec: `axis`
+
+| Field Name | Available Values (type) | Description |
+| --- | --- | --- |
+|`dimension` | `single` | Axis can contains only 1 column |
+|`dimension` | `multiple` | Axis can contains multiple columns |
+|`axisType` | `key` | Column(s) in this axis will be used as `key` like in `PivotTransformation`. These columns will be served in `column.key` |
+|`axisType` | `aggregator` | Column(s) in this axis will be used as `value` like in `PivotTransformation`. These columns will be served in `column.aggregator` |
+|`axisType` | `group` | Column(s) in this axis will be used as `group` like in `PivotTransformation`. These columns will be served in `column.group` |
+|`axisType` | (string) | Any string value can be used here. These columns will be served in `column.custom` |
+|`maxAxisCount` (optional) | (int) | The max number of columns that this axis can contain. (unlimited if `undefined`) |
+|`minAxisCount` (optional) | (int) | The min number of columns that this axis should contain to draw chart. (`1` in case of single dimension) |
+|`description` (optional) | (string) | Description for the axis. |
+
+<br/>
+
+Here is an example.
+
+```js
+axis: {
+ 'xAxis': { dimension: 'multiple', axisType: 'key', },
+ 'yAxis': { dimension: 'multiple', axisType: 'aggregator'},
+ 'category': { dimension: 'multiple', axisType: 'group', maxAxisCount: 2, valueType: 'string', },
+},
+```
+
+<br/>
+
+### Spec: `sharedAxis`
+
+If you set `sharedAxis: false` for sub charts, then their axes are persisted in global space (shared). It's useful for when you creating multiple sub charts sharing their axes but have different parameters. For example,
+
+- `basic-column`, `stacked-column`, `percent-column`
+- `pie` and `donut`
+
+<br/>
+
+Here is an example.
+
+```js
+ const spec = {
+ charts: {
+ 'column': {
+ transform: { method: 'array', },
+ sharedAxis: true,
+ axis: { ... },
+ parameter: { ... },
+ },
+
+ 'stacked': {
+ transform: { method: 'array', },
+ sharedAxis: true,
+ axis: { ... }
+ parameter: { ... },
+ },
+```
+
+<br/>
+
+### Spec: `parameter`
+
+| Field Name | Available Values (type) | Description |
+| --- | --- | --- |
+|`valueType` | `string` | Parameter which has string value |
+|`valueType` | `int` | Parameter which has int value |
+|`valueType` | `float` | Parameter which has float value |
+|`valueType` | `boolean` | Parameter which has boolean value used with `checkbox` widget usually |
+|`valueType` | `JSON` | Parameter which has JSON value used with `textarea` widget usually. `defaultValue` should be `""` (empty string). This ||`defaultValue` | (any) | Default value of this parameter. `JSON` type should have `""` (empty string) |
+|`description` | (string) | Description of this parameter. This value will be parsed as HTML for pretty output |
+|`widget` | `input` | Use [input](https://developer.mozilla.org/en/docs/Web/HTML/Element/input) widget. This is the default widget (if `widget` is undefined)|
+|`widget` | `checkbox` | Use [checkbox](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/checkbox) widget. |
+|`widget` | `textarea` | Use [textarea](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea) widget. |
+|`widget` | `option` | Use [select + option](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select) widget. This parameter should have `optionValues` field as well. |
+|`optionValues` | (Array<string>) | Available option values used with the `option` widget |
+
+<br/>
+
+Here is an example.
+
+```js
+parameter: {
+ // string type, input widget
+ 'xAxisUnit': { valueType: 'string', defaultValue: '', description: 'unit of xAxis', },
+
+ // boolean type, checkbox widget
+ 'inverted': { widget: 'checkbox', valueType: 'boolean', defaultValue: false, description: 'invert x and y axes', },
+
+ // string type, option widget with `optionValues`
+ 'graphType': { widget: 'option', valueType: 'string', defaultValue: 'line', description: 'graph type', optionValues: [ 'line', 'smoothedLine', 'step', ], },
+
+ // HTML in `description`
+ 'dateFormat': { valueType: 'string', defaultValue: '', description: 'format of date (<a href="https://docs.amcharts.com/3/javascriptcharts/AmGraph#dateFormat">doc</a>) (e.g YYYY-MM-DD)', },
+
+ // JSON type, textarea widget
+ 'yAxisGuides': { widget: 'textarea', valueType: 'JSON', defaultValue: '', description: 'guides of yAxis ', },
+```
+
+<br/>
+
+### Spec: `transform`
+
+| Field Name | Available Values (type) | Description |
+| --- | --- | --- |
+|`method` | `object` | designed for rows requiring object manipulation |
+|`method` | `array` | designed for rows requiring array manipulation |
+|`method` | `array:2-key` | designed for xyz charts (e.g bubble chart) |
+|`method` | `drill-down` | designed for drill-down charts |
+|`method` | `raw` | will return the original `tableData.rows` |
+
+<br/>
+
+Whatever you specified as `transform.method`, the `transformer` value will be always function for lazy computation.
+
+```js
+// advanced-transformation.util#getTransformer
+
+if (transformSpec.method === 'raw') {
+ transformer = () => { return rows; }
+} else if (transformSpec.method === 'array') {
+ transformer = () => {
+ ...
+ return { ... }
+ }
+}
+```
+
+Here is actual usage.
+
+```js
+class AwesomeVisualization extends Visualization {
+ constructor(...) { /** setup your spec */ }
+
+ ...
+
+ // `render` will be called whenever `axis` or `parameter` are changed
+ render(data) {
+ const { chart, parameter, column, transformer, } = data
+
+ if (chart === 'line') {
+ const transformed = transformer()
+ // draw line chart
+ } else if (chart === 'no-group') {
+ const transformed = transformer()
+ // draw no-group chart
+ }
+ }
+
+ ...
+}
+```
+
http://git-wip-us.apache.org/repos/asf/zeppelin/blob/45cc8a9e/docs/index.md
----------------------------------------------------------------------
diff --git a/docs/index.md b/docs/index.md
index 8065aef..043538d 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -172,10 +172,11 @@ Join to our [Mailing list](https://zeppelin.apache.org/community.html) and repor
* [Shiro Authentication](./security/shiroauthentication.html)
* [Notebook Authorization](./security/notebook_authorization.html)
* [Data Source Authorization](./security/datasource_authorization.html)
-* Helium Framework
- * [Writing Zeppelin Application (Experimental)](./development/writingzeppelinapplication.html)
- * [Writing Zeppelin Spell (Experimental)](./development/writingzeppelinspell.html)
- * [Writing Zeppelin Visualization (Experimental)](./development/writingzeppelinvisualization.html)
+* Helium Framework (Experimental)
+ * [Writing Zeppelin Application](./development/writingzeppelinapplication.html)
+ * [Writing Zeppelin Spell](./development/writingzeppelinspell.html)
+ * [Writing Zeppelin Visualization: Basic](./development/writingzeppelinvisualization.html)
+ * [Writing Zeppelin Visualization: Transformation](./development/writingzeppelinvisualization_transformation.html)
* Advanced
* [Apache Zeppelin on Vagrant VM](./install/virtual_machine.html)
* [Zeppelin on Spark Cluster Mode (Standalone via Docker)](./install/spark_cluster_mode.html#spark-standalone-mode)
http://git-wip-us.apache.org/repos/asf/zeppelin/blob/45cc8a9e/zeppelin-web/karma.conf.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/karma.conf.js b/zeppelin-web/karma.conf.js
index b24e36a..f47741a 100644
--- a/zeppelin-web/karma.conf.js
+++ b/zeppelin-web/karma.conf.js
@@ -37,6 +37,9 @@ module.exports = function(config) {
// list of files / patterns to load in the browser
files: [
+ // for polyfill
+ 'node_modules/babel-polyfill/dist/polyfill.js',
+
// bower:js
'bower_components/jquery/dist/jquery.js',
'bower_components/es5-shim/es5-shim.js',
@@ -124,8 +127,7 @@ module.exports = function(config) {
preprocessors: {
'src/*/{*.js,!(test)/**/*.js}': 'coverage',
- 'src/index.js': ['webpack', 'sourcemap',],
- 'src/**/*.test.js': ['webpack', 'sourcemap',],
+ 'src/**/*.js': ['webpack', 'sourcemap',],
},
coverageReporter: {
http://git-wip-us.apache.org/repos/asf/zeppelin/blob/45cc8a9e/zeppelin-web/package.json
----------------------------------------------------------------------
diff --git a/zeppelin-web/package.json b/zeppelin-web/package.json
index 6e1252b..99dc058 100644
--- a/zeppelin-web/package.json
+++ b/zeppelin-web/package.json
@@ -16,7 +16,7 @@
"dev:watch": "grunt watch-webpack-dev",
"dev": "npm-run-all --parallel dev:server dev:watch",
"visdev": "npm-run-all --parallel visdev:server dev:watch",
- "pretest": "npm install karma-phantomjs-launcher",
+ "pretest": "npm install karma-phantomjs-launcher babel-polyfill",
"test": "karma start karma.conf.js"
},
"dependencies": {
http://git-wip-us.apache.org/repos/asf/zeppelin/blob/45cc8a9e/zeppelin-web/src/app/tabledata/advanced-transformation-setting.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/tabledata/advanced-transformation-setting.html b/zeppelin-web/src/app/tabledata/advanced-transformation-setting.html
new file mode 100644
index 0000000..8393bf3
--- /dev/null
+++ b/zeppelin-web/src/app/tabledata/advanced-transformation-setting.html
@@ -0,0 +1,280 @@
+<!--
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+<div class="panel panel-default" style="margin-top: 10px; margin-bottom: 11px;">
+
+ <!-- panel: axis (configured column) information -->
+ <div class="panel-heading"
+ style="padding: 6px 12px 6px 12px; font-size: 13px;">
+ <span style="vertical-align: middle; display: inline-block; margin-top: 3px;">Charts</span>
+ <span style="float: right;">
+ <div class="btn-group" role="group" aria-label="...">
+ <div type="button" ng-click="resetAxisConfig()"
+ ng-if="config.panel.columnPanelOpened"
+ class="btn btn-default" style="padding: 2px 5px 2px 5px;">
+ <i class="fa fa-trash-o" aria-hidden="true"></i>
+ </div>
+ <div type="button" ng-if="config.panel.columnPanelOpened"
+ ng-click="toggleColumnPanel()"
+ class="btn btn-default" style="padding: 2px 5px 2px 5px;">
+ <i class="fa fa-minus" style="font-size: 12px;" aria-hidden="true"></i>
+ </div>
+ <div type="button" ng-if="!config.panel.columnPanelOpened"
+ ng-click="toggleColumnPanel()"
+ class="btn btn-default" style="padding: 2px 5px 2px 5px;">
+ <i class="fa fa-expand" style="font-size: 11px;" aria-hidden="true"></i>
+ </div>
+ </div>
+ </span>
+ <div style="clear: both;"></div> <!-- to fix previous span which has float: right -->
+ </div>
+ <div class="panel-body" ng-if="config.panel.columnPanelOpened"
+ style="padding: 8px; margin-top: 3px;">
+ <ul class="noDot">
+ <li class="liVertical" ng-repeat="chart in config.chart.available">
+ <label class="radio-inline">
+ <input type="radio" style="margin-top: 1px; margin-left: -17px;"
+ ng-checked="config.chart.current === chart"
+ ng-click="chartChanged(chart)" value="{{chart}}" />
+ <span style="vertical-align: middle;">
+ {{chart}} {{useSharedAxis(chart) ? '(shared)' : ''}}
+ </span>
+ </label>
+ </li>
+ </ul>
+ </div>
+
+ <!-- panel: available columns -->
+ <div class="panel-heading" ng-if="config.panel.columnPanelOpened"
+ style="padding: 6px 12px 6px 12px; font-size: 13px; border-top: 1px solid #ddd; border-top-left-radius: 0px; border-top-right-radius: 0px;">
+ <span>Available Columns</span>
+ </div>
+ <div class="panel-body" ng-if="config.panel.columnPanelOpened"
+ style="padding: 8px; margin-top: 3px;">
+ <ul class="noDot">
+ <li class="liVertical" ng-repeat="column in columns">
+ <div class="btn btn-default btn-xs"
+ style="background-color: #EFEFEF;"
+ data-drag="true"
+ data-jqyoui-options="{revert: 'invalid', helper: 'clone'}"
+ ng-model="columns"
+ jqyoui-draggable="{index: {{$index}}, placeholder: 'keep'}">
+ {{column.name | limitTo: 30}}{{column.name.length > 30 ? '...' : ''}}
+ </div>
+ </li>
+ </ul>
+ </div>
+
+ <!-- panel: axis (configured columns) -->
+ <hr style="margin: 1px;" ng-if="config.panel.columnPanelOpened" />
+ <div class="panel-body" ng-if="config.panel.columnPanelOpened"
+ style="margin-top: 7px; padding-top: 9px; padding-bottom: 4px;">
+ <div class="row">
+ <div class="col-sm-4 col-md-3"
+ ng-repeat="axisSpec in config.axisSpecs[config.chart.current]">
+ <div class="columns lightBold">
+ <!-- axis name -->
+ <span class="label label-default"
+ uib-tooltip="{{axisSpec.description ? axisSpec.description : ''}}"
+ style="font-weight: 300; font-size: 13px; margin-left: 1px;">
+ {{getAxisAnnotation(axisSpec)}}
+ </span>
+ <span class="label label-default"
+ ng-style="getAxisTypeAnnotationColor(axisSpec)"
+ style="font-weight: 300; font-size: 13px; margin-left: 3px;">
+ {{getAxisTypeAnnotation(axisSpec)}}
+ </span>
+
+ <!-- axis box: in case of single dimension -->
+ <ul data-drop="true"
+ ng-if="isSingleDimensionAxis(axisSpec)"
+ ng-model="config.axis[config.chart.current][axisSpec.name]"
+ jqyoui-droppable="{onDrop:'axisChanged(axisSpec)'}"
+ class="list-unstyled"
+ style="height:36px; border-radius: 6px; margin-top: 7px; overflow: visible !important;">
+ <li ng-if="config.axis[config.chart.current][axisSpec.name]">
+
+ <!-- in case of axis is single dimension and not aggregator -->
+ <div ng-if="!isAggregatorAxis(axisSpec)"
+ class="btn btn-default btn-xs"
+ style="background-color: #EFEFEF;">
+ {{ getSingleDimensionAxis(axisSpec).name }}
+ <span class="fa fa-close" ng-click="removeFromAxis(null, axisSpec)"></span>
+ </div>
+
+ <!-- in case of axis is single dimension and aggregator -->
+ <div class="btn-group">
+ <div ng-if="isAggregatorAxis(axisSpec)"
+ class="btn btn-default btn-xs dropdown-toggle"
+ style="background-color: #EFEFEF; "
+ type="button" data-toggle="dropdown">
+ {{getSingleDimensionAxis(axisSpec).name | limitTo: 30}}{{getSingleDimensionAxis(axisSpec).name > 30 ? '...' : ''}}
+ <span style="color:#717171;">
+ <span class="lightBold" style="text-transform: uppercase;">{{getSingleDimensionAxis(axisSpec).aggr}}</span>
+ </span>
+ <span class="fa fa-close" ng-click="removeFromAxis(null, axisSpec)"></span>
+ </div>
+ <ul class="dropdown-menu" role="menu">
+ <li ng-click="aggregatorChanged(null, axisSpec, 'sum')"><a>sum</a></li>
+ <li ng-click="aggregatorChanged(null, axisSpec, 'count')"><a>count</a></li>
+ <li ng-click="aggregatorChanged(null, axisSpec, 'avg')"><a>avg</a></li>
+ <li ng-click="aggregatorChanged(null, axisSpec, 'min')"><a>min</a></li>
+ <li ng-click="aggregatorChanged(null, axisSpec, 'max')"><a>max</a></li>
+ </ul>
+ </div>
+
+ </li>
+ </ul>
+
+ <!-- axis box: in case of multiple dimensions -->
+ <ul data-drop="true"
+ ng-if="!isSingleDimensionAxis(axisSpec) "
+ ng-model="config.axis[config.chart.current][axisSpec.name]"
+ jqyoui-droppable="{multiple: true, onDrop:'axisChanged(axisSpec)'}"
+ class="list-unstyled"
+ style="height: 108px; border-radius: 6px; margin-top: 7px; overflow: auto !important;">
+
+ <span ng-repeat="col in config.axis[config.chart.current][axisSpec.name]">
+
+ <!-- in case of axis is multiple dimensions and not aggregator -->
+ <span ng-if="!isAggregatorAxis(axisSpec)"
+ class="btn btn-default btn-xs"
+ style="background-color: #EFEFEF; margin: 2px 0px 0px 2px;">
+ {{col.name}}
+ <span class="fa fa-close" ng-click="removeFromAxis($index, axisSpec)"></span>
+ </span>
+
+ <!-- in case of axis is multiple dimension and aggregator -->
+ <span class="btn-group">
+ <span ng-if="isAggregatorAxis(axisSpec)"
+ class="btn btn-default btn-xs dropdown-toggle"
+ style="background-color: #EFEFEF; margin: 2px 0px 0px 2px;"
+ type="button" data-toggle="dropdown">
+ {{col.name | limitTo: 30}}{{col.name.length > 30 ? '...' : ''}}
+ <span style="color:#717171; margin: 0px;">
+ <span class="lightBold"
+ style="text-transform: uppercase; margin: 0px;">{{col.aggr}}
+ </span>
+ </span>
+ <span class="fa fa-close" style="margin: 0px;" ng-click="removeFromAxis($index, axisSpec)"></span>
+ </span>
+ <ul class="dropdown-menu" role="menu">
+ <li ng-click="aggregatorChanged($index, axisSpec, 'sum')"><a>sum</a></li>
+ <li ng-click="aggregatorChanged($index, axisSpec, 'count')"><a>count</a></li>
+ <li ng-click="aggregatorChanged($index, axisSpec, 'avg')"><a>avg</a></li>
+ <li ng-click="aggregatorChanged($index, axisSpec, 'min')"><a>min</a></li>
+ <li ng-click="aggregatorChanged($index, axisSpec, 'max')"><a>max</a></li>
+ </ul>
+ </span>
+
+ </span>
+ </ul>
+
+ </div>
+ </div>
+ </div>
+ </div>
+
+</div>
+
+<!-- panel: parameter information -->
+<div class="panel panel-default">
+
+ <div class="panel-heading" style="padding: 6px 12px 6px 12px; font-size: 13px;">
+ <span style="vertical-align: middle; display: inline-block; margin-top: 3px;">Parameters</span>
+ <span style="float: right;">
+ <div class="btn-group" role="group" aria-label="...">
+ <div type="button" ng-click="parameterChanged()"
+ ng-if="config.panel.parameterPanelOpened"
+ class="btn btn-default" style="padding: 2px 5px 2px 5px;">
+ <i class="fa fa-floppy-o" aria-hidden="true"></i>
+ </div>
+ <div type="button" ng-click="resetParameterConfig()"
+ ng-if="config.panel.parameterPanelOpened"
+ class="btn btn-default" style="padding: 2px 5px 2px 5px;">
+ <i class="fa fa-trash-o" aria-hidden="true"></i>
+ </div>
+ <div type="button" ng-if="config.panel.parameterPanelOpened"
+ ng-click="toggleParameterPanel()"
+ class="btn btn-default" style="padding: 2px 5px 2px 5px;">
+ <i class="fa fa-minus" style="font-size: 12px;" aria-hidden="true"></i>
+ </div>
+ <div type="button" ng-if="!config.panel.parameterPanelOpened"
+ ng-click="toggleParameterPanel()"
+ class="btn btn-default" style="padding: 2px 5px 2px 5px;">
+ <i class="fa fa-expand" style="font-size: 11px;" aria-hidden="true"></i>
+ </div>
+ </div>
+ </span>
+ <div style="clear: both;"></div> <!-- to fix previous span which has float: right -->
+ </div>
+ <div class="panel-body"
+ ng-if="config.panel.parameterPanelOpened"
+ style="padding-top: 13px; padding-bottom: 13px; height: 400px; overflow: auto;">
+ <table class="table table-striped">
+ <tr>
+ <th style="font-size: 12px; font-style: italic">Name</th>
+ <th style="font-size: 12px; font-style: italic">Type</th>
+ <th style="font-size: 12px; font-style: italic">Description</th>
+ <th style="font-size: 12px; font-style: italic">Value</th>
+ </tr>
+ <tr>
+ </tr>
+
+ <tr data-ng-repeat="paramSpec in config.paramSpecs[config.chart.current]">
+ <td style="font-weight: 400; vertical-align: middle;">{{paramSpec.name}}</td>
+ <td style="font-weight: 400; vertical-align: middle;">{{paramSpec.valueType}}</td>
+ <td ng-bind-html="paramSpec.description"
+ style="font-weight: 400; vertical-align: middle;"></td>
+ <td>
+ <div ng-if="isInputWidget(paramSpec)"
+ class="input-group">
+ <input type="text" class="form-control input-sm"
+ style="font-weight: 400; font-size: 12px; vertical-align:middle; border-radius: 5px;"
+ ng-keydown="parameterOnKeyDown($event, paramSpec)"
+ data-ng-model="config.parameter[config.chart.current][paramSpec.name]" />
+ </div>
+ <div class="btn-group"
+ ng-if="isOptionWidget(paramSpec)">
+ <select class="form-control input-sm"
+ ng-keydown="parameterOnKeyDown($event, paramSpec)"
+ ng-change="parameterChanged(paramSpec)"
+ data-ng-model="config.parameter[config.chart.current][paramSpec.name]"
+ data-ng-options="optionValue for optionValue in paramSpec.optionValues"
+ style="font-weight: 400; font-size: 12px;">
+ </select>
+ </div>
+
+ <div ng-if="isCheckboxWidget(paramSpec)">
+ <input type="checkbox"
+ ng-keydown="parameterOnKeyDown($event, paramSpec)"
+ ng-click="parameterChanged(paramSpec)"
+ data-ng-model="config.parameter[config.chart.current][paramSpec.name]"
+ data-ng-checked="config.parameter[config.chart.current][paramSpec.name]" />
+ </div>
+
+ <div ng-if="isTextareaWidget(paramSpec)">
+ <textarea class="form-control" rows="3"
+ ng-keydown="parameterOnKeyDown($event, paramSpec)"
+ data-ng-model="config.parameter[config.chart.current][paramSpec.name]"
+ style="font-weight: 400; font-size: 12px;">
+ </textarea>
+ </div>
+
+ </td>
+ </tr>
+ </table>
+ </div>
+
+</div>