This is an automated email from the ASF dual-hosted git repository.
maximebeauchemin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git
The following commit(s) were added to refs/heads/master by this push:
new 6718588 Add universal "New" button (#6670)
6718588 is described below
commit 67185887812fc41f516567dce95358a9ba2f5c34
Author: Maxime Beauchemin <[email protected]>
AuthorDate: Thu Jan 17 15:48:40 2019 -0800
Add universal "New" button (#6670)
* [cosmetic] CRUD's list from 'Add Filter' to 'Filter Results'
* Add global new button
* A better viz type selector
* lint
* Move view to new module, add unit test
* 'Filter Results' -> 'Filter'
* db merge
* Filter -> 'Filter List'
---
.../addSlice/AddSliceContainer_spec.jsx | 7 +++-
superset/assets/src/addSlice/AddSliceContainer.jsx | 20 +++-------
superset/migrations/versions/8b70aa3d0f87_.py | 22 +++++++++++
.../appbuilder/general/widgets/search.html | 2 +-
superset/templates/appbuilder/navbar_right.html | 15 +++++++-
superset/views/__init__.py | 1 +
superset/views/dashboard.py | 43 ++++++++++++++++++++++
tests/dashboard_tests.py | 10 +++++
8 files changed, 101 insertions(+), 19 deletions(-)
diff --git
a/superset/assets/spec/javascripts/addSlice/AddSliceContainer_spec.jsx
b/superset/assets/spec/javascripts/addSlice/AddSliceContainer_spec.jsx
index c842c04..e16b988 100644
--- a/superset/assets/spec/javascripts/addSlice/AddSliceContainer_spec.jsx
+++ b/superset/assets/spec/javascripts/addSlice/AddSliceContainer_spec.jsx
@@ -20,7 +20,9 @@ import React from 'react';
import { shallow } from 'enzyme';
import { Button } from 'react-bootstrap';
import Select from 'react-virtualized-select';
+
import AddSliceContainer from '../../../src/addSlice/AddSliceContainer';
+import VizTypeControl from
'../../../src/explore/components/controls/VizTypeControl';
const defaultProps = {
datasources: [
@@ -40,8 +42,9 @@ describe('AddSliceContainer', () => {
expect(wrapper.state().visType).toBe('table');
});
- it('renders 2 selects', () => {
- expect(wrapper.find(Select)).toHaveLength(2);
+ it('renders a select and a VizTypeControl', () => {
+ expect(wrapper.find(Select)).toHaveLength(1);
+ expect(wrapper.find(VizTypeControl)).toHaveLength(1);
});
it('renders a button', () => {
diff --git a/superset/assets/src/addSlice/AddSliceContainer.jsx
b/superset/assets/src/addSlice/AddSliceContainer.jsx
index bb3166c..6935690 100644
--- a/superset/assets/src/addSlice/AddSliceContainer.jsx
+++ b/superset/assets/src/addSlice/AddSliceContainer.jsx
@@ -21,7 +21,8 @@ import PropTypes from 'prop-types';
import { Button, Panel } from 'react-bootstrap';
import Select from 'react-virtualized-select';
import { t } from '@superset-ui/translation';
-import { getChartMetadataRegistry } from '@superset-ui/chart';
+
+import VizTypeControl from '../explore/components/controls/VizTypeControl';
const propTypes = {
datasources: PropTypes.arrayOf(PropTypes.shape({
@@ -65,8 +66,8 @@ export default class AddSliceContainer extends
React.PureComponent {
});
}
- changeVisType(e) {
- this.setState({ visType: e.value });
+ changeVisType(visType) {
+ this.setState({ visType });
}
isBtnDisabled() {
@@ -74,12 +75,6 @@ export default class AddSliceContainer extends
React.PureComponent {
}
render() {
- const types = getChartMetadataRegistry().entries()
- .map(({ key, value }) => ({
- value: key,
- label: value.name,
- }));
-
return (
<div className="container">
<Panel header={<h3>{t('Create a new chart')}</h3>}>
@@ -108,17 +103,14 @@ export default class AddSliceContainer extends
React.PureComponent {
<br />
<div>
<p>{t('Choose a visualization type')}</p>
- <Select
- clearable={false}
+ <VizTypeControl
name="select-vis-type"
- style={styleSelectWidth}
onChange={this.changeVisType}
- options={types}
- placeholder={t('Choose a visualization type')}
value={this.state.visType}
/>
</div>
<br />
+ <hr />
<Button
bsStyle="primary"
disabled={this.isBtnDisabled()}
diff --git a/superset/migrations/versions/8b70aa3d0f87_.py
b/superset/migrations/versions/8b70aa3d0f87_.py
new file mode 100644
index 0000000..9368e60
--- /dev/null
+++ b/superset/migrations/versions/8b70aa3d0f87_.py
@@ -0,0 +1,22 @@
+"""empty message
+
+Revision ID: 8b70aa3d0f87
+Revises: ('fbd55e0f83eb', 'fb13d49b72f9')
+Create Date: 2019-01-17 08:31:55.781032
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '8b70aa3d0f87'
+down_revision = ('fbd55e0f83eb', 'fb13d49b72f9')
+
+from alembic import op
+import sqlalchemy as sa
+
+
+def upgrade():
+ pass
+
+
+def downgrade():
+ pass
diff --git a/superset/templates/appbuilder/general/widgets/search.html
b/superset/templates/appbuilder/general/widgets/search.html
index 688936f..eb703dc 100644
--- a/superset/templates/appbuilder/general/widgets/search.html
+++ b/superset/templates/appbuilder/general/widgets/search.html
@@ -3,7 +3,7 @@
<div class="list-search-container">
<form id="filter_form" class="form-search" method="get">
<button type="button" class="btn btn-default dropdown-toggle"
data-toggle="dropdown">
- <i class="fa fa-filter text-primary" aria-hidden="true"></i> {{_("Add
Filter")}}
+ <i class="fa fa-filter text-primary" aria-hidden="true"></i>
{{_("Filter List")}}
</button>
<ul class="dropdown-menu">
diff --git a/superset/templates/appbuilder/navbar_right.html
b/superset/templates/appbuilder/navbar_right.html
index 1d6fb69..bc188ef 100644
--- a/superset/templates/appbuilder/navbar_right.html
+++ b/superset/templates/appbuilder/navbar_right.html
@@ -3,6 +3,19 @@
{% if not locale %}
{% set locale = 'en' %}
{% endif %}
+
+{% if not current_user.is_anonymous %}
+ <li class="dropdown">
+ <button type="button" style="margin-top: 12px; margin-right: 30px;"
data-toggle="dropdown" class="dropdown-toggle btn btn-sm btn-primary">
+ <i class="fa fa-plus"></i> {{ _("New") }}
+ </button>
+ <ul class="dropdown-menu">
+ <li><a href="/superset/sqllab"><span class="fa fa-fw
fa-search"></span> {{_("SQL Query")}}</a></li>
+ <li><a href="/chart/add"><span class="fa fa-fw
fa-bar-chart"></span> {{_("Chart")}}</a></li>
+ <li><a href="/dashboard/new/"><span class="fa fa-fw
fa-dashboard"></span> {{_("Dashboard")}}</a></li>
+ </ul>
+ </li>
+{% endif %}
{% if languages.keys()|length > 1 %}
<li class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown"
href="javascript:void(0)">
@@ -25,8 +38,6 @@
</li>
{% endif %}
-
-
{% if not current_user.is_anonymous %}
<li class="dropdown">
<a
diff --git a/superset/views/__init__.py b/superset/views/__init__.py
index 8b51016..386e16e 100644
--- a/superset/views/__init__.py
+++ b/superset/views/__init__.py
@@ -18,6 +18,7 @@ from . import base # noqa
from . import api # noqa
from . import core # noqa
from . import sql_lab # noqa
+from . import dashboard # noqa
from . import annotations # noqa
from . import datasource # noqa
from . import schedules # noqa
diff --git a/superset/views/dashboard.py b/superset/views/dashboard.py
new file mode 100644
index 0000000..f4b992d
--- /dev/null
+++ b/superset/views/dashboard.py
@@ -0,0 +1,43 @@
+# pylint: disable=C,R,W
+# 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.
+from flask import g, redirect
+from flask_appbuilder import expose
+from flask_appbuilder.security.decorators import has_access
+
+from superset import appbuilder, db
+from superset.models import core as models
+from .base import BaseSupersetView
+
+
+class Dashboard(BaseSupersetView):
+ """The base views for Superset!"""
+
+ @has_access
+ @expose('/new/')
+ def new(self):
+ """Creates a new, blank dashboard and redirects to it in edit mode"""
+ new_dashboard = models.Dashboard(
+ dashboard_title='[ untitled dashboard ]',
+ owners=[g.user],
+ )
+ db.session.add(new_dashboard)
+ db.session.commit()
+ return redirect(f'/superset/dashboard/{new_dashboard.id}/?edit=true')
+
+
+appbuilder.add_view_no_menu(Dashboard)
diff --git a/tests/dashboard_tests.py b/tests/dashboard_tests.py
index 2d6fe1a..bf3e7d1 100644
--- a/tests/dashboard_tests.py
+++ b/tests/dashboard_tests.py
@@ -19,6 +19,7 @@ import json
import unittest
from flask import escape
+from sqlalchemy import func
from superset import db, security_manager
from superset.connectors.sqla.models import SqlaTable
@@ -68,6 +69,15 @@ class DashboardTests(SupersetTestCase):
for title, url in urls.items():
assert escape(title) in self.client.get(url).data.decode('utf-8')
+ def test_new_dashboard(self):
+ self.login(username='admin')
+ dash_count_before =
db.session.query(func.count(models.Dashboard.id)).first()[0]
+ url = '/dashboard/new/'
+ resp = self.get_resp(url)
+ self.assertIn('[ untitled dashboard ]', resp)
+ dash_count_after =
db.session.query(func.count(models.Dashboard.id)).first()[0]
+ self.assertEquals(dash_count_before + 1, dash_count_after)
+
def test_dashboard_modes(self):
self.login(username='admin')
dash = (