mirror of
https://github.com/apache/superset.git
synced 2024-09-17 11:09:47 -04:00
[slices] add simple new slice form (#2800)
* initial structure for add new slice page * simplify add slice form * add a test * fix long line * use underscore for template name * fix controls path * fix vis types select
This commit is contained in:
parent
7c28e4eace
commit
5bf40e2256
97
superset/assets/javascripts/addSlice/AddSliceContainer.jsx
Normal file
97
superset/assets/javascripts/addSlice/AddSliceContainer.jsx
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { Button, Panel, Grid, Row, Col } from 'react-bootstrap';
|
||||||
|
import Select from 'react-virtualized-select';
|
||||||
|
import visTypes from '../explore/stores/visTypes';
|
||||||
|
|
||||||
|
const propTypes = {
|
||||||
|
datasources: PropTypes.arrayOf(PropTypes.shape({
|
||||||
|
label: PropTypes.string.isRequired,
|
||||||
|
value: PropTypes.string.isRequired,
|
||||||
|
})).isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default class AddSliceContainer extends React.PureComponent {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
const visTypeKeys = Object.keys(visTypes);
|
||||||
|
this.vizTypeOptions = visTypeKeys.map(vt => ({ label: visTypes[vt].label, value: vt }));
|
||||||
|
this.state = {
|
||||||
|
datasourceValue: this.props.datasources[0].value,
|
||||||
|
datasourceId: this.props.datasources[0].value.split('__')[0],
|
||||||
|
datasourceType: this.props.datasources[0].value.split('__')[1],
|
||||||
|
visType: 'table',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
exploreUrl() {
|
||||||
|
const baseUrl = `/superset/explore/${this.state.datasourceType}/${this.state.datasourceId}`;
|
||||||
|
const formData = encodeURIComponent(JSON.stringify({ viz_type: this.state.visType }));
|
||||||
|
return `${baseUrl}?form_data=${formData}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
gotoSlice() {
|
||||||
|
window.location.href = this.exploreUrl();
|
||||||
|
}
|
||||||
|
|
||||||
|
changeDatasource(e) {
|
||||||
|
this.setState({
|
||||||
|
datasourceValue: e.value,
|
||||||
|
datasourceId: e.value.split('__')[0],
|
||||||
|
datasourceType: e.value.split('__')[1],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
changeSliceName(e) {
|
||||||
|
this.setState({ sliceName: e.target.value });
|
||||||
|
}
|
||||||
|
|
||||||
|
changeVisType(e) {
|
||||||
|
this.setState({ visType: e.value });
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className="container">
|
||||||
|
<Panel header={<h3>Create a new slice</h3>}>
|
||||||
|
<Grid>
|
||||||
|
<Row>
|
||||||
|
<Col xs={12} sm={6}>
|
||||||
|
<div>
|
||||||
|
<p>Choose a datasource</p>
|
||||||
|
<Select
|
||||||
|
clearable={false}
|
||||||
|
name="select-datasource"
|
||||||
|
onChange={this.changeDatasource.bind(this)}
|
||||||
|
options={this.props.datasources}
|
||||||
|
placeholder="Choose a datasource"
|
||||||
|
value={this.state.datasourceValue}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<div>
|
||||||
|
<p>Choose a visualization type</p>
|
||||||
|
<Select
|
||||||
|
clearable={false}
|
||||||
|
name="select-vis-type"
|
||||||
|
onChange={this.changeVisType.bind(this)}
|
||||||
|
options={this.vizTypeOptions}
|
||||||
|
placeholder="Choose a visualization type"
|
||||||
|
value={this.state.visType}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<Button bsStyle="primary" onClick={this.gotoSlice.bind(this)}>
|
||||||
|
Create new slice
|
||||||
|
</Button>
|
||||||
|
<br /><br />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Grid>
|
||||||
|
</Panel>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AddSliceContainer.propTypes = propTypes;
|
14
superset/assets/javascripts/addSlice/index.jsx
Normal file
14
superset/assets/javascripts/addSlice/index.jsx
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
import { appSetup } from '../common';
|
||||||
|
import AddSliceContainer from './AddSliceContainer';
|
||||||
|
|
||||||
|
appSetup();
|
||||||
|
|
||||||
|
const addSliceContainer = document.getElementById('js-add-slice-container');
|
||||||
|
const bootstrapData = JSON.parse(addSliceContainer.getAttribute('data-bootstrap'));
|
||||||
|
|
||||||
|
ReactDOM.render(
|
||||||
|
<AddSliceContainer datasources={bootstrapData.datasources} />,
|
||||||
|
addSliceContainer,
|
||||||
|
);
|
@ -0,0 +1,39 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { expect } from 'chai';
|
||||||
|
import { describe, it, beforeEach } from 'mocha';
|
||||||
|
import { shallow } from 'enzyme';
|
||||||
|
import { Button } from 'react-bootstrap';
|
||||||
|
import Select from 'react-virtualized-select';
|
||||||
|
import AddSliceContainer from '../../../javascripts/addSlice/AddSliceContainer';
|
||||||
|
|
||||||
|
const defaultProps = {
|
||||||
|
datasources: [
|
||||||
|
{ label: 'my first table', value: '1__table' },
|
||||||
|
{ label: 'another great table', value: '2__table' },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('AddSliceContainer', () => {
|
||||||
|
let wrapper;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
wrapper = shallow(<AddSliceContainer {...defaultProps} />);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses table as default visType', () => {
|
||||||
|
expect(wrapper.state().visType).to.equal('table');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders 2 selects', () => {
|
||||||
|
expect(wrapper.find(Select)).to.have.lengthOf(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders a button', () => {
|
||||||
|
expect(wrapper.find(Button)).to.have.lengthOf(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('formats explore url', () => {
|
||||||
|
const formattedUrl = '/superset/explore/table/1?form_data=%7B%22viz_type%22%3A%22table%22%7D';
|
||||||
|
expect(wrapper.instance().exploreUrl()).to.equal(formattedUrl);
|
||||||
|
});
|
||||||
|
});
|
@ -14,6 +14,7 @@ const config = {
|
|||||||
entry: {
|
entry: {
|
||||||
'css-theme': APP_DIR + '/javascripts/css-theme.js',
|
'css-theme': APP_DIR + '/javascripts/css-theme.js',
|
||||||
common: APP_DIR + '/javascripts/common.js',
|
common: APP_DIR + '/javascripts/common.js',
|
||||||
|
addSlice: ['babel-polyfill', APP_DIR + '/javascripts/addSlice/index.jsx'],
|
||||||
dashboard: ['babel-polyfill', APP_DIR + '/javascripts/dashboard/Dashboard.jsx'],
|
dashboard: ['babel-polyfill', APP_DIR + '/javascripts/dashboard/Dashboard.jsx'],
|
||||||
explore: ['babel-polyfill', APP_DIR + '/javascripts/explore/index.jsx'],
|
explore: ['babel-polyfill', APP_DIR + '/javascripts/explore/index.jsx'],
|
||||||
sqllab: ['babel-polyfill', APP_DIR + '/javascripts/SqlLab/index.jsx'],
|
sqllab: ['babel-polyfill', APP_DIR + '/javascripts/SqlLab/index.jsx'],
|
||||||
|
19
superset/templates/superset/add_slice.html
Normal file
19
superset/templates/superset/add_slice.html
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{% extends "superset/basic.html" %}
|
||||||
|
|
||||||
|
{% block title %}
|
||||||
|
Add new slice
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
<div
|
||||||
|
id="js-add-slice-container"
|
||||||
|
data-bootstrap="{{ bootstrap_data }}"
|
||||||
|
></div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block tail_js %}
|
||||||
|
{{ super() }}
|
||||||
|
{% with filename="addSlice" %}
|
||||||
|
{% include "superset/partials/_script_tag.html" %}
|
||||||
|
{% endwith %}
|
||||||
|
{% endblock %}
|
@ -372,11 +372,17 @@ class SliceModelView(SupersetModelView, DeleteMixin): # noqa
|
|||||||
@expose('/add', methods=['GET', 'POST'])
|
@expose('/add', methods=['GET', 'POST'])
|
||||||
@has_access
|
@has_access
|
||||||
def add(self):
|
def add(self):
|
||||||
flash(__(
|
datasources = ConnectorRegistry.get_all_datasources(db.session)
|
||||||
"To create a new slice, you can open a data source "
|
datasources = [
|
||||||
"through the `Sources` menu, or alter an existing slice "
|
{'value': str(d.id) + '__' + d.type, 'label': repr(d)}
|
||||||
"from the `Slices` menu"), "info")
|
for d in datasources
|
||||||
return redirect('/superset/welcome')
|
]
|
||||||
|
return self.render_template(
|
||||||
|
"superset/add_slice.html",
|
||||||
|
bootstrap_data=json.dumps({
|
||||||
|
'datasources': datasources,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
appbuilder.add_view(
|
appbuilder.add_view(
|
||||||
SliceModelView,
|
SliceModelView,
|
||||||
|
Loading…
Reference in New Issue
Block a user