mirror of
https://github.com/apache/superset.git
synced 2024-09-19 12:09:42 -04:00
feat: updated viz plugin generator (#636)
* feat: hello world plugin * Fix yeoman generator * Adding Emotion to template and template result * adding `reactify` technique to example code * fixing errant comma * annoying lint issue * React needed for JSX * questionable typing fix * labelling options * freshly generated hello world * linting and freshly generated plugin * add comments to buildQuery * Add docs * nixing word cloud console logs * lint annoyance * a note on multiple plugin exports * second option for building a plugin is now enabled * Officially supporting viz plugins ;) * fresh plugin build * Adding note about controls, and TODOs for example controls * linting * new lock file * adding file structure to readme * more transform props notes * better notes, better linting * Adding a third option for rendering plugin * fresh package rendering * manually updated plugin. Now let's see if the generator matches it! * template changes, fresh plugin render. * moving file tree to the right place * touchups to template (killing third option) * thumbnail update * Spit out form data, for good measure. * more clarity * dummy controls * bold control works, fresh docs, fresh build! * typing for fancy props * bump superset-ui deps to 0.14 * Implement header text and font size * Add tests + mcense headers + inor cleanup * Replace YourPluginName with packageLabel in README * remove trailing spaces * fix hard coded reference to HelloWorld * removing legacy generators * plugin comment tweaks * typescript comments, minor formatting * adding option for badges in readme * generator offers choice of function or class plugin * Add timeseries option * killing the plugin... we'll make that a separate PR. * add new tests * remove unnecessary imports * fix timeseries type * comment on value/label in select options Co-authored-by: Jesse Yang <jesse.yang@airbnb.com> * moving renderTrigger note up to first instance * control-utils -> chart-controls * Wrapper -> Styles nomenclature * Stronger typing (H/T @ktmud) * ControlPanelConfig type on control config * nixing requiresTime * moving Styles component to be external, passing it props * lint nits * typing tweak Co-authored-by: Ville Brofeldt <ville.v.brofeldt@gmail.com> Co-authored-by: Jesse Yang <jesse.yang@airbnb.com>
This commit is contained in:
parent
a540cc283c
commit
fb69984857
@ -7,7 +7,8 @@
|
|||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
First, install [Yeoman](http://yeoman.io) and `generator-superset` using [npm](https://www.npmjs.com/) (we assume you have pre-installed [node.js](https://nodejs.org/)).
|
First, install [Yeoman](http://yeoman.io) and `generator-superset` using
|
||||||
|
[npm](https://www.npmjs.com/) (we assume you have pre-installed [node.js](https://nodejs.org/)).
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install -g yo
|
npm install -g yo
|
||||||
@ -16,7 +17,7 @@ npm install -g @superset-ui/generator-superset
|
|||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
Generate a new package in `@superset-ui`
|
Generate a new package or visualization plugin in `@superset-ui`
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd superset-ui/packages
|
cd superset-ui/packages
|
||||||
|
@ -25,14 +25,6 @@ module.exports = class extends Generator {
|
|||||||
name: 'Create superset-ui chart plugin package',
|
name: 'Create superset-ui chart plugin package',
|
||||||
value: 'plugin-chart',
|
value: 'plugin-chart',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: 'Create superset-ui-legacy package',
|
|
||||||
value: 'legacy-plugin-chart',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Create superset-ui-legacy chart demo in storybook',
|
|
||||||
value: 'legacy-plugin-chart-demo',
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
@ -1,40 +0,0 @@
|
|||||||
/* eslint-disable sort-keys */
|
|
||||||
|
|
||||||
const Generator = require('yeoman-generator');
|
|
||||||
const chalk = require('chalk');
|
|
||||||
const yosay = require('yosay');
|
|
||||||
const _ = require('lodash');
|
|
||||||
|
|
||||||
module.exports = class extends Generator {
|
|
||||||
async prompting() {
|
|
||||||
// Have Yeoman greet the user.
|
|
||||||
this.log(yosay(`Welcome to the rad ${chalk.red('generator-superset')} generator!`));
|
|
||||||
|
|
||||||
this.option('skipInstall');
|
|
||||||
|
|
||||||
this.answers = await this.prompt([
|
|
||||||
{
|
|
||||||
type: 'input',
|
|
||||||
name: 'packageName',
|
|
||||||
message: 'Package name:',
|
|
||||||
default: _.kebabCase(this.appname.replace('legacy plugin chart', '').trim()), // Default to current folder name
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'input',
|
|
||||||
name: 'packageLabel',
|
|
||||||
message: 'Package label:',
|
|
||||||
default: _.upperFirst(_.camelCase(this.appname.replace('legacy plugin chart', '').trim())), // Default to current folder name
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
writing() {
|
|
||||||
this.fs.copyTpl(this.templatePath('index.js'), this.destinationPath('index.js'), this.answers);
|
|
||||||
this.fs.copyTpl(this.templatePath('data.js'), this.destinationPath('data.js'), this.answers);
|
|
||||||
this.fs.copyTpl(
|
|
||||||
this.templatePath('Stories.jsx'),
|
|
||||||
this.destinationPath('Stories.jsx'),
|
|
||||||
this.answers,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,22 +0,0 @@
|
|||||||
/* eslint-disable no-magic-numbers */
|
|
||||||
import React from 'react';
|
|
||||||
import { SuperChart } from '@superset-ui/chart';
|
|
||||||
import data from './data';
|
|
||||||
|
|
||||||
export default [
|
|
||||||
{
|
|
||||||
renderStory: () => (
|
|
||||||
<SuperChart
|
|
||||||
chartType="<%= packageName %>"
|
|
||||||
chartProps={{
|
|
||||||
formData: {},
|
|
||||||
height: 400,
|
|
||||||
payload: { data },
|
|
||||||
width: 400,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
storyName: 'Basic',
|
|
||||||
storyPath: 'plugin-chart-<%= packageName %>|<%= packageLabel %>ChartPlugin',
|
|
||||||
},
|
|
||||||
];
|
|
@ -1,2 +0,0 @@
|
|||||||
/* eslint-disable sort-keys, no-magic-numbers */
|
|
||||||
export default {};
|
|
@ -1,8 +0,0 @@
|
|||||||
import <%= packageLabel %>ChartPlugin from '../../../../superset-ui-legacy-plugin-chart-<%= packageName %>';
|
|
||||||
import Stories from './Stories';
|
|
||||||
|
|
||||||
new <%= packageLabel %>ChartPlugin().configure({ key: '<%= packageName %>' }).register();
|
|
||||||
|
|
||||||
export default {
|
|
||||||
examples: [...Stories],
|
|
||||||
};
|
|
@ -1,39 +0,0 @@
|
|||||||
/* eslint-disable sort-keys */
|
|
||||||
|
|
||||||
const Generator = require('yeoman-generator');
|
|
||||||
const _ = require('lodash');
|
|
||||||
|
|
||||||
module.exports = class extends Generator {
|
|
||||||
async prompting() {
|
|
||||||
this.option('skipInstall');
|
|
||||||
|
|
||||||
this.answers = await this.prompt([
|
|
||||||
{
|
|
||||||
type: 'input',
|
|
||||||
name: 'packageName',
|
|
||||||
message: 'Package name:',
|
|
||||||
default: _.kebabCase(this.appname.replace('superset ui legacy plugin chart', '').trim()), // Default to current folder name
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'input',
|
|
||||||
name: 'description',
|
|
||||||
message: 'Description:',
|
|
||||||
default: _.upperFirst(
|
|
||||||
_.startCase(this.appname.replace('superset ui legacy plugin chart', '').trim()),
|
|
||||||
), // Default to current folder name
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
writing() {
|
|
||||||
this.fs.copyTpl(
|
|
||||||
this.templatePath('_package.json'),
|
|
||||||
this.destinationPath('package.json'),
|
|
||||||
this.answers,
|
|
||||||
);
|
|
||||||
this.fs.copyTpl(this.templatePath('README.md'), this.destinationPath('README.md'), {
|
|
||||||
...this.answers,
|
|
||||||
packageLabel: _.upperFirst(_.camelCase(this.answers.packageName)),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,34 +0,0 @@
|
|||||||
## @superset-ui/legacy-plugin-chart-<%= packageName %>
|
|
||||||
|
|
||||||
[![Version](https://img.shields.io/npm/v/@superset-ui/legacy-plugin-chart-<%= packageName %>.svg?style=flat-square)](https://img.shields.io/npm/v/@superset-ui/legacy-plugin-chart-<%= packageName %>.svg?style=flat-square)
|
|
||||||
[![David (path)](https://img.shields.io/david/apache-superset/superset-ui.svg?path=packages%2Fsuperset-ui-legacy-plugin-chart-<%= packageName %>&style=flat-square)](https://david-dm.org/apache-superset/superset-ui?path=packages/superset-ui-legacy-plugin-chart-<%= packageName %>)
|
|
||||||
|
|
||||||
This plugin provides <%= description %> for Superset.
|
|
||||||
|
|
||||||
### Usage
|
|
||||||
|
|
||||||
Configure `key`, which can be any `string`, and register the plugin. This `key` will be used to lookup this chart throughout the app.
|
|
||||||
|
|
||||||
```js
|
|
||||||
import <%= packageLabel %>ChartPlugin from '@superset-ui/legacy-plugin-chart-<%= packageName %>';
|
|
||||||
|
|
||||||
new <%= packageLabel %>ChartPlugin()
|
|
||||||
.configure({ key: '<%= packageName %>' })
|
|
||||||
.register();
|
|
||||||
```
|
|
||||||
|
|
||||||
Then use it via `SuperChart`. See [storybook](https://apache-superset.github.io/superset-ui-legacy/?selectedKind=plugin-chart-<%= packageName %>) for more details.
|
|
||||||
|
|
||||||
```js
|
|
||||||
<SuperChart
|
|
||||||
chartType="<%= packageName %>"
|
|
||||||
chartProps={{
|
|
||||||
width: 600,
|
|
||||||
height: 600,
|
|
||||||
formData: {...},
|
|
||||||
payload: {
|
|
||||||
data: {...},
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
```
|
|
@ -1,39 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@superset-ui/legacy-plugin-chart-<%= packageName %>",
|
|
||||||
"version": "0.0.0",
|
|
||||||
"description": "Superset Legacy Chart - <%= description %>",
|
|
||||||
"sideEffects": false,
|
|
||||||
"main": "lib/index.js",
|
|
||||||
"module": "esm/index.js",
|
|
||||||
"files": [
|
|
||||||
"esm",
|
|
||||||
"lib"
|
|
||||||
],
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "git+https://github.com/apache-superset/superset-ui-legacy.git"
|
|
||||||
},
|
|
||||||
"keywords": [
|
|
||||||
"superset"
|
|
||||||
],
|
|
||||||
"author": "Superset",
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"bugs": {
|
|
||||||
"url": "https://github.com/apache-superset/superset-ui-legacy/issues"
|
|
||||||
},
|
|
||||||
"homepage": "https://github.com/apache-superset/superset-ui-legacy#readme",
|
|
||||||
"publishConfig": {
|
|
||||||
"access": "public"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"prop-types": "^15.6.2"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@superset-ui/chart": "latest",
|
|
||||||
"@superset-ui/translation": "latest"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@superset-ui/chart": "latest",
|
|
||||||
"@superset-ui/translation": "latest"
|
|
||||||
}
|
|
||||||
}
|
|
@ -22,6 +22,42 @@ module.exports = class extends Generator {
|
|||||||
// Default to current folder name
|
// Default to current folder name
|
||||||
default: _.upperFirst(_.startCase(this.appname.replace('plugin chart', '').trim())),
|
default: _.upperFirst(_.startCase(this.appname.replace('plugin chart', '').trim())),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: 'list',
|
||||||
|
name: 'componentType',
|
||||||
|
message: 'What type of React component would you like?',
|
||||||
|
choices: [
|
||||||
|
{
|
||||||
|
name: 'Class component',
|
||||||
|
value: 'class',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Function component (with hooks)',
|
||||||
|
value: 'function',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'list',
|
||||||
|
name: 'chartType',
|
||||||
|
message: 'What type of chart would you like?',
|
||||||
|
choices: [
|
||||||
|
{
|
||||||
|
name: 'Time-series chart',
|
||||||
|
value: 'timeseries',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Regular chart',
|
||||||
|
value: 'regular',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'confirm',
|
||||||
|
name: 'addBadges',
|
||||||
|
message: "Add superset-ui badges to your plugin's README.md",
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,10 +73,14 @@ module.exports = class extends Generator {
|
|||||||
['package.erb', 'package.json'],
|
['package.erb', 'package.json'],
|
||||||
['README.erb', 'README.md'],
|
['README.erb', 'README.md'],
|
||||||
['src/index.erb', 'src/index.ts'],
|
['src/index.erb', 'src/index.ts'],
|
||||||
|
['src/plugin/buildQuery.erb', 'src/plugin/buildQuery.ts'],
|
||||||
|
['src/plugin/controlPanel.erb', 'src/plugin/controlPanel.ts'],
|
||||||
['src/plugin/index.erb', 'src/plugin/index.ts'],
|
['src/plugin/index.erb', 'src/plugin/index.ts'],
|
||||||
['src/plugin/transformProps.txt', 'src/plugin/transformProps.ts'],
|
['src/plugin/transformProps.erb', 'src/plugin/transformProps.ts'],
|
||||||
['src/MyChart.erb', `src/${packageLabel}.tsx`],
|
['src/MyChart.erb', `src/${packageLabel}.tsx`],
|
||||||
['test/index.erb', 'test/index.test.ts'],
|
['test/index.erb', 'test/index.test.ts'],
|
||||||
|
['test/plugin/buildQuery.test.erb', 'test/plugin/buildQuery.test.ts'],
|
||||||
|
['test/plugin/transformProps.test.erb', 'test/plugin/transformProps.test.ts'],
|
||||||
].forEach(([src, dest]) => {
|
].forEach(([src, dest]) => {
|
||||||
this.fs.copyTpl(this.templatePath(src), this.destinationPath(dest), params);
|
this.fs.copyTpl(this.templatePath(src), this.destinationPath(dest), params);
|
||||||
});
|
});
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
## @superset-ui/plugin-chart-<%= packageName %>
|
## @superset-ui/plugin-chart-<%= packageName %>
|
||||||
|
|
||||||
[![Version](https://img.shields.io/npm/v/@superset-ui/plugin-chart-<%= packageName %>.svg?style=flat-square)](https://img.shields.io/npm/v/@superset-ui/plugin-chart-<%= packageName %>.svg?style=flat-square)
|
<%if (addBadges) { %>[![Version](https://img.shields.io/npm/v/@superset-ui/plugin-chart-<%= packageName %>.svg?style=flat-square)](https://img.shields.io/npm/v/@superset-ui/plugin-chart-<%= packageName %>.svg?style=flat-square)
|
||||||
[![David (path)](https://img.shields.io/david/apache-superset/superset-ui.svg?path=packages%2Fsuperset-ui-plugin-chart-<%= packageName %>&style=flat-square)](https://david-dm.org/apache-superset/superset-ui?path=packages/superset-ui-plugin-chart-<%= packageName %>)
|
[![David (path)](https://img.shields.io/david/apache-superset/superset-ui.svg?path=packages%2Fsuperset-ui-plugin-chart-<%= packageName %>&style=flat-square)](https://david-dm.org/apache-superset/superset-ui?path=packages/superset-ui-plugin-chart-<%= packageName %>)<% } %>
|
||||||
|
|
||||||
This plugin provides <%= description %> for Superset.
|
This plugin provides <%= description %> for Superset.
|
||||||
|
|
||||||
@ -29,4 +29,26 @@ Then use it via `SuperChart`. See [storybook](https://apache-superset.github.io/
|
|||||||
data: {...},
|
data: {...},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
```
|
||||||
|
|
||||||
|
### File structure generated
|
||||||
|
|
||||||
|
```
|
||||||
|
├── README.md
|
||||||
|
├── package.json
|
||||||
|
├── src
|
||||||
|
│ ├── <%= packageLabel %>.tsx
|
||||||
|
│ ├── images
|
||||||
|
│ │ └── thumbnail.png
|
||||||
|
│ ├── index.ts
|
||||||
|
│ ├── plugin
|
||||||
|
│ │ ├── buildQuery.ts
|
||||||
|
│ │ ├── controlPanel.ts
|
||||||
|
│ │ ├── index.ts
|
||||||
|
│ │ └── transformProps.ts
|
||||||
|
│ └── types.ts
|
||||||
|
├── test
|
||||||
|
│ └── index.test.ts
|
||||||
|
└── types
|
||||||
|
└── external.d.ts
|
||||||
```
|
```
|
@ -26,7 +26,16 @@
|
|||||||
"access": "public"
|
"access": "public"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@superset-ui/chart": "latest",
|
"@superset-ui/chart": "^0.14.1",
|
||||||
"@superset-ui/translation": "latest"
|
"@superset-ui/query": "^0.14.1",
|
||||||
|
"@superset-ui/chart-controls": "^0.14.0",
|
||||||
|
"@superset-ui/translation": "^0.14.0",
|
||||||
|
"@superset-ui/validator": "^0.14.1",
|
||||||
|
"@superset-ui/style": "^0.14.0",
|
||||||
|
"react": "^16.13.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/jest": "^26.0.0",
|
||||||
|
"jest": "^26.0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,25 +16,115 @@
|
|||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
import React from 'react';
|
import React, { <%if (componentType == 'class') { %>PureComponent<% } %><%if (componentType == 'function') { %>useEffect<% } %>, createRef } from 'react';
|
||||||
|
import styled, { supersetTheme } from '@superset-ui/style';
|
||||||
|
|
||||||
|
interface <%= packageLabel %>StylesProps {
|
||||||
|
height: number;
|
||||||
|
width: number;
|
||||||
|
headerFontSize: keyof typeof supersetTheme.typography.sizes;
|
||||||
|
boldText: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export type <%= packageLabel %>Props = {
|
export type <%= packageLabel %>Props = {
|
||||||
height: number;
|
height: number;
|
||||||
width: number;
|
width: number;
|
||||||
data: { x: number; y: number }[];
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
data: Record<any, any>; // please add additional typing for your data here
|
||||||
|
// add typing here for the props you pass in from transformProps.ts!
|
||||||
|
boldText: boolean;
|
||||||
|
headerFontSize: 'xxs' | 'xs' | 's' | 'm' | 'l' | 'xl' | 'xxl';
|
||||||
|
headerText: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default class <%= packageLabel %> extends React.PureComponent<<%= packageLabel %>Props> {
|
// The following Styles component is a <div> element, which has been styled using Emotion
|
||||||
|
// For docs, visit https://emotion.sh/docs/styled
|
||||||
|
|
||||||
|
// Theming variables are provided for your use via a ThemeProvider
|
||||||
|
// imported from @superset-ui/style. For variables available, please visit
|
||||||
|
// https://github.com/apache-superset/superset-ui/blob/master/packages/superset-ui-style/src/index.ts
|
||||||
|
|
||||||
|
const Styles = styled.div<<%= packageLabel %>StylesProps>`
|
||||||
|
background-color: ${({ theme }) => theme.colors.secondary.light2};
|
||||||
|
padding: ${({ theme }) => theme.gridUnit * 4}px;
|
||||||
|
border-radius: ${({ theme }) => theme.gridUnit * 2}px;
|
||||||
|
height: ${({ height }) => height};
|
||||||
|
width: ${({ width }) => width};
|
||||||
|
overflow-y: scroll;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
/* You can use your props to control CSS! */
|
||||||
|
font-size: ${({ theme, headerFontSize }) => theme.typography.sizes[headerFontSize]};
|
||||||
|
font-weight: ${({ theme, boldText }) => theme.typography.weights[boldText ? 'bold' : 'normal']};
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ******************* WHAT YOU CAN BUILD HERE *******************
|
||||||
|
* In essence, a chart is given a few key ingredients to work with:
|
||||||
|
* * Data: provided via `props.data`
|
||||||
|
* * A DOM element
|
||||||
|
* * FormData (your controls!) provided as props by transformProps.ts
|
||||||
|
*/
|
||||||
|
|
||||||
|
<%if (componentType == 'class') { %>export default class <%= packageLabel %> extends PureComponent<<%= packageLabel %>Props> {
|
||||||
|
// Often, you just want to get a hold of the DOM and go nuts.
|
||||||
|
// Here, you can do that with createRef, and componentDidMount.
|
||||||
|
|
||||||
|
rootElem = createRef<HTMLDivElement>();
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
const root = this.rootElem.current as HTMLElement;
|
||||||
|
console.log('Plugin element', root);
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
// height and width are the height and width of the DOM element as it exists in the dashboard.
|
||||||
|
// There is also a `data` prop, which is, of course, your DATA 🎉
|
||||||
|
console.log('Approach 1 props', this.props);
|
||||||
const { data, height, width } = this.props;
|
const { data, height, width } = this.props;
|
||||||
|
|
||||||
|
console.log('Plugin props', this.props);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ backgroundColor: '#ffe459', padding: 16, borderRadius: 8, height, width }}>
|
<Styles
|
||||||
<h3>Hello!</h3>
|
ref={this.rootElem}
|
||||||
<pre>
|
boldText={this.props.boldText}
|
||||||
{JSON.stringify(this.props, null, 2)}
|
headerFontSize={this.props.headerFontSize}
|
||||||
</pre>
|
height={height}
|
||||||
</div>
|
width={width}
|
||||||
|
>
|
||||||
|
<h3>{this.props.headerText}</h3>
|
||||||
|
<pre>{JSON.stringify(data, null, 2)}</pre>
|
||||||
|
</Styles>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}<% } %><%if (componentType == 'function') { %>export default function <%= packageLabel %>(props: <%= packageLabel %>Props) {
|
||||||
|
// height and width are the height and width of the DOM element as it exists in the dashboard.
|
||||||
|
// There is also a `data` prop, which is, of course, your DATA 🎉
|
||||||
|
const { data, height, width } = props;
|
||||||
|
|
||||||
|
const rootElem = createRef<HTMLDivElement>();
|
||||||
|
|
||||||
|
// Often, you just want to get a hold of the DOM and go nuts.
|
||||||
|
// Here, you can do that with createRef, and the useEffect hook.
|
||||||
|
useEffect(() => {
|
||||||
|
const root = rootElem.current as HTMLElement;
|
||||||
|
console.log('Plugin element', root);
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('Plugin props', props);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Styles
|
||||||
|
ref={rootElem}
|
||||||
|
boldText={props.boldText}
|
||||||
|
headerFontSize={props.headerFontSize}
|
||||||
|
height={height}
|
||||||
|
width={width}
|
||||||
|
>
|
||||||
|
<h3>{props.headerText}</h3>
|
||||||
|
<pre>${JSON.stringify(data, null, 2)}</pre>
|
||||||
|
</Styles>
|
||||||
|
);
|
||||||
|
}<% } %>
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 20 KiB |
@ -1 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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.
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line import/prefer-default-export
|
||||||
export { default as <%= packageLabel %>ChartPlugin } from './plugin';
|
export { default as <%= packageLabel %>ChartPlugin } from './plugin';
|
||||||
|
/**
|
||||||
|
* Note: this file exports the default export from <%= packageLabel %>.tsx.
|
||||||
|
* If you want to export multiple visualization modules, you will need to
|
||||||
|
* either add additional plugin folders (similar in structure to ./plugin)
|
||||||
|
* OR export multiple instances of `ChartPlugin` extensions in ./plugin/index.ts
|
||||||
|
* which in turn load exports from <%= packageLabel %>.tsx
|
||||||
|
*/
|
||||||
|
@ -0,0 +1,43 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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.
|
||||||
|
*/
|
||||||
|
import { buildQueryContext, QueryFormData } from '@superset-ui/query';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The buildQuery function is used to create an instance of QueryContext that's
|
||||||
|
* sent to the chart data endpoint. In addition to containing information of which
|
||||||
|
* datasource to use, it specifies the type (e.g. full payload, samples, query) and
|
||||||
|
* format (e.g. CSV or JSON) of the result and whether or not to force refresh the data from
|
||||||
|
* the datasource as opposed to using a cached copy of the data, if available.
|
||||||
|
*
|
||||||
|
* More importantly though, QueryContext contains a property `queries`, which is an array of
|
||||||
|
* QueryObjects specifying individual data requests to be made. A QueryObject specifies which
|
||||||
|
* columns, metrics and filters, among others, to use during the query. Usually it will be enough
|
||||||
|
* to specify just one query based on the baseQueryObject, but for some more advanced use cases
|
||||||
|
* it is possible to define post processing operations in the QueryObject, or multiple queries
|
||||||
|
* if a viz needs multiple different result sets.
|
||||||
|
*/
|
||||||
|
export default function buildQuery(formData: QueryFormData) {
|
||||||
|
return buildQueryContext(formData, baseQueryObject => [
|
||||||
|
{
|
||||||
|
...baseQueryObject,
|
||||||
|
<%if (chartType === 'timeseries') { %> // Time series charts need to set the `is_timeseries` flag to true
|
||||||
|
is_timeseries: true,
|
||||||
|
<% } %> },
|
||||||
|
]);
|
||||||
|
}
|
@ -0,0 +1,177 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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.
|
||||||
|
*/
|
||||||
|
import { t } from '@superset-ui/translation';
|
||||||
|
import { validateNonEmpty } from '@superset-ui/validator';
|
||||||
|
import { ControlPanelConfig } from '@superset-ui/chart-controls';
|
||||||
|
|
||||||
|
const config: ControlPanelConfig = {
|
||||||
|
/**
|
||||||
|
* The control panel is split into two tabs: "Query" and
|
||||||
|
* "Chart Options". The controls that define the inputs to
|
||||||
|
* the chart data request, such as columns and metrics, usually
|
||||||
|
* reside within "Query", while controls that affect the visual
|
||||||
|
* appearance or functionality of the chart are under the
|
||||||
|
* "Chart Options" section.
|
||||||
|
*
|
||||||
|
* There are several predefined controls that can be used.
|
||||||
|
* Some examples:
|
||||||
|
* - groupby: columns to group by (tranlated to GROUP BY statement)
|
||||||
|
* - series: same as groupby, but single selection.
|
||||||
|
* - metrics: multiple metrics (translated to aggregate expression)
|
||||||
|
* - metric: sane as metrics, but single selection
|
||||||
|
* - adhoc_filters: filters (translated to WHERE or HAVING
|
||||||
|
* depending on filter type)
|
||||||
|
* - row_limit: maximum number of rows (translated to LIMIT statement)
|
||||||
|
*
|
||||||
|
* If a control panel has both a `series` and `groupby` control, and
|
||||||
|
* the user has chosen `col1` as the value for the `series` control,
|
||||||
|
* and `col2` and `col3` as values for the `groupby` control,
|
||||||
|
* the resulting query will contain three groupby columns. This is due
|
||||||
|
* to the `series` control having the property `queryField` set to
|
||||||
|
* `groupby`, which automatically appends the values from the
|
||||||
|
* `series` control to the `groupby` control when the query is generated.
|
||||||
|
*
|
||||||
|
* It is also possible to define custom controls by importing the
|
||||||
|
* necessary dependencies and overriding the default parameters, which
|
||||||
|
* can then be placed in the `controlSetRows` section
|
||||||
|
* of the `Query` section instead of a predefined control.
|
||||||
|
*
|
||||||
|
* import { validateNonEmpty } from '@superset-ui/validator';
|
||||||
|
* import { sharedControls, ControlConfig, ControlPanelConfig } from '@superset-ui/chart-controls';
|
||||||
|
*
|
||||||
|
* const myControl: ControlConfig<'SelectControl'> = {
|
||||||
|
* name: 'secondary_entity',
|
||||||
|
* config: {
|
||||||
|
* ...sharedControls.entity,
|
||||||
|
* type: 'SelectControl',
|
||||||
|
* label: t('Secondary Entity'),
|
||||||
|
* mapStateToProps: state => ({
|
||||||
|
* sharedControls.columnChoices(state.datasource)
|
||||||
|
* .columns.filter(c => c.groupby)
|
||||||
|
* })
|
||||||
|
* validators: [validateNonEmpty],
|
||||||
|
* },
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* In addition to the basic drop down control, there are several predefined
|
||||||
|
* control types (can be set via the `type` property) that can be used. Some
|
||||||
|
* commonly used examples:
|
||||||
|
* - SelectControl: Dropdown to select single or multiple values,
|
||||||
|
usually columns
|
||||||
|
* - MetricsControl: Dropdown to select metrics, triggering a modal
|
||||||
|
to define Metric details
|
||||||
|
* - AdhocFilterControl: Control to choose filters
|
||||||
|
* - CheckboxControl: A checkbox for choosing true/false values
|
||||||
|
* - SliderControl: A slider with min/max values
|
||||||
|
* - TextControl: Control for text data
|
||||||
|
*
|
||||||
|
* For more control input types, check out the `incubator-superset` repo
|
||||||
|
* and open this file: superset-frontend/src/explore/components/controls/index.js
|
||||||
|
*
|
||||||
|
* To ensure all controls have been filled out correctly, the following
|
||||||
|
* validators are provided
|
||||||
|
* by the `@superset-ui/validator` package:
|
||||||
|
* - validateNonEmpty: must have at least one value
|
||||||
|
* - validateInteger: must be an integer value
|
||||||
|
* - validateNumber: must be an intger or decimal value
|
||||||
|
*/
|
||||||
|
|
||||||
|
// For control input types, see: superset-frontend/src/explore/components/controls/index.js
|
||||||
|
controlPanelSections: [
|
||||||
|
{
|
||||||
|
label: t('Query'),
|
||||||
|
expanded: true,
|
||||||
|
controlSetRows: [['groupby'], ['metrics'], ['adhoc_filters'], ['row_limit', null]],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('Hello Controls!'),
|
||||||
|
expanded: true,
|
||||||
|
controlSetRows: [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
name: 'header_text',
|
||||||
|
config: {
|
||||||
|
type: 'TextControl',
|
||||||
|
default: 'Hello, World!',
|
||||||
|
renderTrigger: true,
|
||||||
|
// ^ this makes it apply instantaneously, without triggering a "run query" button
|
||||||
|
label: t('Header Text'),
|
||||||
|
description: t('The text you want to see in the header'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
name: 'bold_text',
|
||||||
|
config: {
|
||||||
|
type: 'CheckboxControl',
|
||||||
|
label: t('Bold Text'),
|
||||||
|
renderTrigger: true,
|
||||||
|
default: true,
|
||||||
|
description: t('A checkbox to make the '),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
name: 'header_font_size',
|
||||||
|
config: {
|
||||||
|
type: 'SelectControl',
|
||||||
|
label: t('Font Size'),
|
||||||
|
default: 'xl',
|
||||||
|
choices: [
|
||||||
|
// [value, label]
|
||||||
|
['xxs', 'xx-small'],
|
||||||
|
['xs', 'x-small'],
|
||||||
|
['s', 'small'],
|
||||||
|
['m', 'medium'],
|
||||||
|
['l', 'large'],
|
||||||
|
['xl', 'x-large'],
|
||||||
|
['xxl', 'xx-large'],
|
||||||
|
],
|
||||||
|
renderTrigger: true,
|
||||||
|
description: t('The size of your header font'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
<%if (chartType === 'timeseries') { %> // Time series charts need to override the `druidTimeSeries` and `sqlaTimeSeries`
|
||||||
|
// sections to add the time grain dropdown.
|
||||||
|
sectionOverrides: {
|
||||||
|
druidTimeSeries: {
|
||||||
|
controlSetRows: [['granularity', 'druid_time_origin'], ['time_range']],
|
||||||
|
},
|
||||||
|
sqlaTimeSeries: {
|
||||||
|
controlSetRows: [['granularity_sqla', 'time_grain_sqla'], ['time_range']],
|
||||||
|
},
|
||||||
|
},<% } %>
|
||||||
|
controlOverrides: {
|
||||||
|
series: {
|
||||||
|
validators: [validateNonEmpty],
|
||||||
|
clearable: false,
|
||||||
|
},
|
||||||
|
row_limit: {
|
||||||
|
default: 100,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
@ -18,6 +18,8 @@
|
|||||||
*/
|
*/
|
||||||
import { t } from '@superset-ui/translation';
|
import { t } from '@superset-ui/translation';
|
||||||
import { ChartMetadata, ChartPlugin } from '@superset-ui/chart';
|
import { ChartMetadata, ChartPlugin } from '@superset-ui/chart';
|
||||||
|
import buildQuery from './buildQuery';
|
||||||
|
import controlPanel from './controlPanel';
|
||||||
import transformProps from './transformProps';
|
import transformProps from './transformProps';
|
||||||
import thumbnail from '../images/thumbnail.png';
|
import thumbnail from '../images/thumbnail.png';
|
||||||
|
|
||||||
@ -28,8 +30,20 @@ const metadata = new ChartMetadata({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export default class <%= packageLabel %>ChartPlugin extends ChartPlugin {
|
export default class <%= packageLabel %>ChartPlugin extends ChartPlugin {
|
||||||
|
/**
|
||||||
|
* The constructor is used to pass relevant metadata and callbacks that get
|
||||||
|
* registered in respective registries that are used throughout the library
|
||||||
|
* and application. A more thorough description of each property is given in
|
||||||
|
* the respective imported file.
|
||||||
|
*
|
||||||
|
* It is worth noting that `buildQuery` and is optional, and only needed for
|
||||||
|
* advanced visualizations that require either post processing operations
|
||||||
|
* (pivoting, rolling aggregations, sorting etc) or submitting multiple queries.
|
||||||
|
*/
|
||||||
constructor() {
|
constructor() {
|
||||||
super({
|
super({
|
||||||
|
buildQuery,
|
||||||
|
controlPanel,
|
||||||
loadChart: () => import('../<%= packageLabel %>'),
|
loadChart: () => import('../<%= packageLabel %>'),
|
||||||
metadata,
|
metadata,
|
||||||
transformProps,
|
transformProps,
|
||||||
|
@ -0,0 +1,77 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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.
|
||||||
|
*/
|
||||||
|
import { ChartProps, DataRecord } from '@superset-ui/chart';
|
||||||
|
|
||||||
|
<%if (chartType === 'timeseries') { %>type TimestampType = string | number | Date;
|
||||||
|
|
||||||
|
interface <%= packageLabel %>Datum extends DataRecord {
|
||||||
|
__timestamp: TimestampType;
|
||||||
|
}<% } else { %>type <%= packageLabel %>Datum = DataRecord;<% } %>
|
||||||
|
|
||||||
|
export default function transformProps(chartProps: ChartProps) {
|
||||||
|
/**
|
||||||
|
* This function is called after a successful response has been
|
||||||
|
* received from the chart data endpoint, and is used to transform
|
||||||
|
* the incoming data prior to being sent to the Visualization.
|
||||||
|
*
|
||||||
|
* The transformProps function is also quite useful to return
|
||||||
|
* additional/modified props to your data viz component. The formData
|
||||||
|
* can also be accessed from your <%= packageLabel %>.tsx file, but
|
||||||
|
* doing supplying custom props here is often handy for integrating third
|
||||||
|
* party libraries that rely on specific props.
|
||||||
|
*
|
||||||
|
* A description of properties in `chartProps`:
|
||||||
|
* - `height`, `width`: the height/width of the DOM element in which
|
||||||
|
* the chart is located
|
||||||
|
* - `formData`: the chart data request payload that was sent to the
|
||||||
|
* backend.
|
||||||
|
* - `queryData`: the chart data response payload that was received
|
||||||
|
* from the backend. Some notable properties of `queryData`:
|
||||||
|
* - `data`: an array with data, each row with an object mapping
|
||||||
|
* the column/alias to its value. Example:
|
||||||
|
* `[{ col1: 'abc', metric1: 10 }, { col1: 'xyz', metric1: 20 }]`
|
||||||
|
* - `rowcount`: the number of rows in `data`
|
||||||
|
* - `query`: the query that was issued.
|
||||||
|
*
|
||||||
|
* Please note: the transformProps function gets cached when the
|
||||||
|
* application loads. When making changes to the `transformProps`
|
||||||
|
* function during development with hot reloading, changes won't
|
||||||
|
* be seen until restarting the development server.
|
||||||
|
*/
|
||||||
|
const { width, height, formData, queryData } = chartProps;
|
||||||
|
const data = queryData.data as <%= packageLabel %>Datum[];
|
||||||
|
|
||||||
|
console.log('formData via TransformProps.ts', formData);
|
||||||
|
|
||||||
|
return {
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
<%if (chartType === 'timeseries') { %>
|
||||||
|
data: data.map((item: { __timestamp: TimestampType }) => ({
|
||||||
|
...item,
|
||||||
|
// convert epoch to native Date
|
||||||
|
// eslint-disable-next-line no-underscore-dangle
|
||||||
|
__timestamp: new Date(item.__timestamp),
|
||||||
|
})),<% } else { %> data,<% } %>
|
||||||
|
// and now your control data, manipulated as needed, and passed through as props!
|
||||||
|
boldText: formData.boldText,
|
||||||
|
headerFontSize: formData.headerFontSize,
|
||||||
|
headerText: formData.headerText,
|
||||||
|
};
|
||||||
|
}
|
@ -1,32 +0,0 @@
|
|||||||
import { ChartProps } from '@superset-ui/chart';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
|
||||||
* or more contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. The ASF licenses this file
|
|
||||||
* to you 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.
|
|
||||||
*/
|
|
||||||
export default function transformProps(chartProps: ChartProps) {
|
|
||||||
const { width, height, formData, queryData } = chartProps;
|
|
||||||
const { color } = formData;
|
|
||||||
const { data } = queryData;
|
|
||||||
|
|
||||||
return {
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
color,
|
|
||||||
data,
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,5 +1,31 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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.
|
||||||
|
*/
|
||||||
import { <%= packageLabel %>ChartPlugin } from '../src';
|
import { <%= packageLabel %>ChartPlugin } from '../src';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The example tests in this file act as a starting point, and
|
||||||
|
* we encourage you to build more. These tests check that the
|
||||||
|
* plugin loads properly, and focus on `transformProps`
|
||||||
|
* to ake sure that data, controls, and props are all
|
||||||
|
* treated correctly (e.g. formData from plugin controls
|
||||||
|
* properly transform the data and/or any resulting props).
|
||||||
|
*/
|
||||||
describe('@superset-ui/plugin-chart-<%= packageName %>', () => {
|
describe('@superset-ui/plugin-chart-<%= packageName %>', () => {
|
||||||
it('exists', () => {
|
it('exists', () => {
|
||||||
expect(<%= packageLabel %>ChartPlugin).toBeDefined();
|
expect(<%= packageLabel %>ChartPlugin).toBeDefined();
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
import 'babel-polyfill';
|
||||||
|
import buildQuery from '../../src/plugin/buildQuery';
|
||||||
|
|
||||||
|
describe('<%= packageLabel %> buildQuery', () => {
|
||||||
|
const formData = {
|
||||||
|
datasource: '5__table',
|
||||||
|
granularity_sqla: 'ds',
|
||||||
|
series: 'foo',
|
||||||
|
viz_type: 'my_chart',
|
||||||
|
queryFields: { series: 'groupby' },
|
||||||
|
};
|
||||||
|
|
||||||
|
it('should build groupby with series in form data', () => {
|
||||||
|
const queryContext = buildQuery(formData);
|
||||||
|
const [query] = queryContext.queries;
|
||||||
|
expect(query.groupby).toEqual(['foo']);
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,35 @@
|
|||||||
|
import 'babel-polyfill';
|
||||||
|
import { ChartProps } from '@superset-ui/chart';
|
||||||
|
import transformProps from '../../src/plugin/transformProps';
|
||||||
|
|
||||||
|
describe('<%= packageLabel %> tranformProps', () => {
|
||||||
|
const formData = {
|
||||||
|
colorScheme: 'bnbColors',
|
||||||
|
datasource: '3__table',
|
||||||
|
granularity_sqla: 'ds',
|
||||||
|
metric: 'sum__num',
|
||||||
|
series: 'name',
|
||||||
|
boldText: true,
|
||||||
|
headerFontSize: 'xs',
|
||||||
|
headerText: 'my text',
|
||||||
|
};
|
||||||
|
const chartProps = new ChartProps({
|
||||||
|
formData,
|
||||||
|
width: 800,
|
||||||
|
height: 600,
|
||||||
|
queryData: {
|
||||||
|
data: [{ name: 'Hulk', sum__num: 1<%if (chartType === 'timeseries') { %>, __timestamp: 599616000000<% } %> }],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should tranform chart props for viz', () => {
|
||||||
|
expect(transformProps(chartProps)).toEqual({
|
||||||
|
width: 800,
|
||||||
|
height: 600,
|
||||||
|
boldText: true,
|
||||||
|
headerFontSize: 'xs',
|
||||||
|
headerText: 'my text',
|
||||||
|
data: [{ name: 'Hulk', sum__num: 1<%if (chartType === 'timeseries') { %>, __timestamp: new Date(599616000000)<% } %> }],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -1,35 +0,0 @@
|
|||||||
const path = require('path');
|
|
||||||
const assert = require('yeoman-assert');
|
|
||||||
const helpers = require('yeoman-test');
|
|
||||||
|
|
||||||
describe('generator-superset:legacy-plugin-chart-demo', () => {
|
|
||||||
let dir;
|
|
||||||
|
|
||||||
beforeAll(() => {
|
|
||||||
dir = process.cwd();
|
|
||||||
|
|
||||||
return helpers
|
|
||||||
.run(path.join(__dirname, '../generators/legacy-plugin-chart-demo'))
|
|
||||||
.withPrompts({ packageName: '4d-pie-chart', packageLabel: '4DPieChart' })
|
|
||||||
.withOptions({ skipInstall: true });
|
|
||||||
});
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Change working directory back to original working directory
|
|
||||||
* after the test has completed.
|
|
||||||
* yeoman tests switch to tmp directory and write files there.
|
|
||||||
* Usually this is fine for solo package.
|
|
||||||
* However, for a monorepo like this one,
|
|
||||||
* it made jest confuses with current directory
|
|
||||||
* (being in tmp directory instead of superset-ui root)
|
|
||||||
* and interferes with other tests in sibling packages
|
|
||||||
* that are run after the yeoman tests.
|
|
||||||
*/
|
|
||||||
afterAll(() => {
|
|
||||||
process.chdir(dir);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('creates files', () => {
|
|
||||||
assert.file(['index.js', 'Stories.jsx']);
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,35 +0,0 @@
|
|||||||
const path = require('path');
|
|
||||||
const assert = require('yeoman-assert');
|
|
||||||
const helpers = require('yeoman-test');
|
|
||||||
|
|
||||||
describe('generator-superset:legacy-plugin-chart', () => {
|
|
||||||
let dir;
|
|
||||||
|
|
||||||
beforeAll(() => {
|
|
||||||
dir = process.cwd();
|
|
||||||
|
|
||||||
return helpers
|
|
||||||
.run(path.join(__dirname, '../generators/legacy-plugin-chart'))
|
|
||||||
.withPrompts({ packageName: '4d-pie-chart', description: '4D Pie Chart' })
|
|
||||||
.withOptions({ skipInstall: true });
|
|
||||||
});
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Change working directory back to original working directory
|
|
||||||
* after the test has completed.
|
|
||||||
* yeoman tests switch to tmp directory and write files there.
|
|
||||||
* Usually this is fine for solo package.
|
|
||||||
* However, for a monorepo like this one,
|
|
||||||
* it made jest confuses with current directory
|
|
||||||
* (being in tmp directory instead of superset-ui root)
|
|
||||||
* and interferes with other tests in sibling packages
|
|
||||||
* that are run after the yeoman tests.
|
|
||||||
*/
|
|
||||||
afterAll(() => {
|
|
||||||
process.chdir(dir);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('creates files', () => {
|
|
||||||
assert.file(['package.json']);
|
|
||||||
});
|
|
||||||
});
|
|
@ -33,11 +33,15 @@ describe('generator-superset:plugin-chart', () => {
|
|||||||
assert.file([
|
assert.file([
|
||||||
'package.json',
|
'package.json',
|
||||||
'README.md',
|
'README.md',
|
||||||
|
'src/plugin/buildQuery.ts',
|
||||||
|
'src/plugin/controlPanel.ts',
|
||||||
'src/plugin/index.ts',
|
'src/plugin/index.ts',
|
||||||
'src/plugin/transformProps.ts',
|
'src/plugin/transformProps.ts',
|
||||||
'src/index.ts',
|
|
||||||
'src/ColdMap.tsx',
|
'src/ColdMap.tsx',
|
||||||
|
'src/index.ts',
|
||||||
'test/index.test.ts',
|
'test/index.test.ts',
|
||||||
|
'test/plugin/buildQuery.test.ts',
|
||||||
|
'test/plugin/transformProps.test.ts',
|
||||||
'types/external.d.ts',
|
'types/external.d.ts',
|
||||||
'src/images/thumbnail.png',
|
'src/images/thumbnail.png',
|
||||||
]);
|
]);
|
||||||
|
@ -205,7 +205,6 @@ type SelectOption = AnyDict | string | [ReactText, ReactNode];
|
|||||||
type SelectControlType =
|
type SelectControlType =
|
||||||
| 'SelectControl'
|
| 'SelectControl'
|
||||||
| 'SelectAsyncControl'
|
| 'SelectAsyncControl'
|
||||||
| 'SelectControl'
|
|
||||||
| 'MetricsControl'
|
| 'MetricsControl'
|
||||||
| 'FixedOrMetricControl'
|
| 'FixedOrMetricControl'
|
||||||
| 'AdhocFilterControl'
|
| 'AdhocFilterControl'
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user