You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by gr...@apache.org on 2017/11/16 08:27:17 UTC
[incubator-superset] branch master updated: Further refactoring
around dashboards (#3843)
This is an automated email from the ASF dual-hosted git repository.
graceguo 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 1c545d3 Further refactoring around dashboards (#3843)
1c545d3 is described below
commit 1c545d3a2d4337322ad361c62b6380bbef2c9584
Author: Maxime Beauchemin <ma...@gmail.com>
AuthorDate: Thu Nov 16 00:27:15 2017 -0800
Further refactoring around dashboards (#3843)
I was wondering what was left to do in order to remove Dashboard.jsx
and superset.js, and it looks like they can just be pulled out.
I am so happy to get rid of what used to be the messiest JS files in the
whole repo.
Thanks @graceguo!
---
.../assets/javascripts/dashboard/Dashboard.jsx | 381 ---------------------
superset/assets/javascripts/modules/superset.js | 262 --------------
2 files changed, 643 deletions(-)
diff --git a/superset/assets/javascripts/dashboard/Dashboard.jsx b/superset/assets/javascripts/dashboard/Dashboard.jsx
deleted file mode 100644
index 9e67647..0000000
--- a/superset/assets/javascripts/dashboard/Dashboard.jsx
+++ /dev/null
@@ -1,381 +0,0 @@
-import React from 'react';
-import { render } from 'react-dom';
-import d3 from 'd3';
-import { Alert } from 'react-bootstrap';
-import moment from 'moment';
-
-import GridLayout from './components/GridLayout';
-import Header from './components/Header';
-import { appSetup } from '../common';
-import AlertsWrapper from '../components/AlertsWrapper';
-import { t } from '../locales';
-import '../../stylesheets/dashboard.css';
-
-const superset = require('../modules/superset');
-const urlLib = require('url');
-const utils = require('../modules/utils');
-
-let px;
-
-appSetup();
-
-export function getInitialState(boostrapData) {
- const dashboard = Object.assign(
- {},
- utils.controllerInterface,
- boostrapData.dashboard_data,
- { common: boostrapData.common });
- dashboard.firstLoad = true;
-
- dashboard.posDict = {};
- if (dashboard.position_json) {
- dashboard.position_json.forEach((position) => {
- dashboard.posDict[position.slice_id] = position;
- });
- }
- dashboard.refreshTimer = null;
- const state = Object.assign({}, boostrapData, { dashboard });
- return state;
-}
-
-function unload() {
- const message = t('You have unsaved changes.');
- window.event.returnValue = message; // Gecko + IE
- return message; // Gecko + Webkit, Safari, Chrome etc.
-}
-
-function onBeforeUnload(hasChanged) {
- if (hasChanged) {
- window.addEventListener('beforeunload', unload);
- } else {
- window.removeEventListener('beforeunload', unload);
- }
-}
-
-function renderAlert() {
- render(
- <div className="container-fluid">
- <Alert bsStyle="warning">
- <strong>{t('You have unsaved changes.')}</strong> {t('Click the')}
- <i className="fa fa-save" />
- {t('button on the top right to save your changes.')}
- </Alert>
- </div>,
- document.getElementById('alert-container'),
- );
-}
-
-function initDashboardView(dashboard) {
- render(
- <div>
- <AlertsWrapper initMessages={dashboard.common.flash_messages} />
- <Header dashboard={dashboard} />
- </div>,
- document.getElementById('dashboard-header'),
- );
- // eslint-disable-next-line no-param-reassign
- dashboard.reactGridLayout = render(
- <GridLayout dashboard={dashboard} />,
- document.getElementById('grid-container'),
- );
-
- // Displaying widget controls on hover
- $('.react-grid-item').hover(
- function () {
- $(this).find('.chart-controls').fadeIn(300);
- },
- function () {
- $(this).find('.chart-controls').fadeOut(300);
- },
- );
- $('div.grid-container').css('visibility', 'visible');
-
- $('div.widget').click(function (e) {
- const $this = $(this);
- const $target = $(e.target);
-
- if ($target.hasClass('slice_info')) {
- $this.find('.slice_description').slideToggle(0, function () {
- $this.find('.refresh').click();
- });
- } else if ($target.hasClass('controls-toggle')) {
- $this.find('.chart-controls').toggle();
- }
- });
- px.initFavStars();
- $('[data-toggle="tooltip"]').tooltip({ container: 'body' });
-}
-
-export function dashboardContainer(dashboard, datasources, userid) {
- return Object.assign({}, dashboard, {
- type: 'dashboard',
- filters: {},
- curUserId: userid,
- init() {
- this.sliceObjects = [];
- dashboard.slices.forEach((data) => {
- if (data.error) {
- const html = `<div class="alert alert-danger">${data.error}</div>`;
- $(`#slice_${data.slice_id}`).find('.token').html(html);
- } else {
- const slice = px.Slice(data, datasources[data.form_data.datasource], this);
- $(`#slice_${data.slice_id}`).find('a.refresh').click(() => {
- slice.render(true);
- });
- this.sliceObjects.push(slice);
- }
- });
- this.loadPreSelectFilters();
- this.renderSlices(this.sliceObjects);
- this.firstLoad = false;
- this.bindResizeToWindowResize();
- },
- onChange() {
- onBeforeUnload(true);
- renderAlert();
- },
- onSave() {
- onBeforeUnload(false);
- $('#alert-container').html('');
- },
- loadPreSelectFilters() {
- try {
- const filters = JSON.parse(px.getParam('preselect_filters') || '{}');
- for (const sliceId in filters) {
- for (const col in filters[sliceId]) {
- this.setFilter(sliceId, col, filters[sliceId][col], false, false);
- }
- }
- } catch (e) {
- // console.error(e);
- }
- },
- setFilter(sliceId, col, vals, refresh) {
- this.addFilter(sliceId, col, vals, false, refresh);
- },
- done(slice) {
- const refresh = slice.getWidgetHeader().find('.refresh');
- const data = slice.data;
- const cachedWhen = moment.utc(data.cached_dttm).fromNow();
- if (data !== undefined && data.is_cached) {
- refresh
- .addClass('danger')
- .attr(
- 'title',
- t('Served from data cached %s . Click to force refresh.', cachedWhen))
- .tooltip('fixTitle');
- } else {
- refresh
- .removeClass('danger')
- .attr('title', t('Click to force refresh'))
- .tooltip('fixTitle');
- }
- },
- effectiveExtraFilters(sliceId) {
- const f = [];
- const immuneSlices = this.metadata.filter_immune_slices || [];
- if (sliceId && immuneSlices.includes(sliceId)) {
- // The slice is immune to dashboard filters
- return f;
- }
-
- // Building a list of fields the slice is immune to filters on
- let immuneToFields = [];
- if (
- sliceId &&
- this.metadata.filter_immune_slice_fields &&
- this.metadata.filter_immune_slice_fields[sliceId]) {
- immuneToFields = this.metadata.filter_immune_slice_fields[sliceId];
- }
- for (const filteringSliceId in this.filters) {
- if (filteringSliceId === sliceId.toString()) {
- // Filters applied by the slice don't apply to itself
- continue;
- }
- for (const field in this.filters[filteringSliceId]) {
- if (!immuneToFields.includes(field)) {
- f.push({
- col: field,
- op: 'in',
- val: this.filters[filteringSliceId][field],
- });
- }
- }
- }
- return f;
- },
- addFilter(sliceId, col, vals, merge = true, refresh = true) {
- if (
- this.getSlice(sliceId) && (
- ['__from', '__to', '__time_col', '__time_grain', '__time_origin', '__granularity']
- .indexOf(col) >= 0 ||
- this.getSlice(sliceId).formData.groupby.indexOf(col) !== -1
- )
- ) {
- if (!(sliceId in this.filters)) {
- this.filters[sliceId] = {};
- }
- if (!(col in this.filters[sliceId]) || !merge) {
- this.filters[sliceId][col] = vals;
-
- // d3.merge pass in array of arrays while some value form filter components
- // from and to filter box require string to be process and return
- } else if (this.filters[sliceId][col] instanceof Array) {
- this.filters[sliceId][col] = d3.merge([this.filters[sliceId][col], vals]);
- } else {
- this.filters[sliceId][col] = d3.merge([[this.filters[sliceId][col]], vals])[0] || '';
- }
- if (refresh) {
- this.refreshExcept(sliceId);
- }
- }
- this.updateFilterParamsInUrl();
- },
- readFilters() {
- // Returns a list of human readable active filters
- return JSON.stringify(this.filters, null, ' ');
- },
- updateFilterParamsInUrl() {
- const urlObj = urlLib.parse(location.href, true);
- urlObj.query = urlObj.query || {};
- urlObj.query.preselect_filters = this.readFilters();
- urlObj.search = null;
- history.pushState(urlObj.query, window.title, urlLib.format(urlObj));
- },
- bindResizeToWindowResize() {
- let resizeTimer;
- const dash = this;
- $(window).on('resize', () => {
- clearTimeout(resizeTimer);
- resizeTimer = setTimeout(() => {
- dash.sliceObjects.forEach((slice) => {
- slice.resize();
- });
- }, 500);
- });
- },
- stopPeriodicRender() {
- if (this.refreshTimer) {
- clearTimeout(this.refreshTimer);
- this.refreshTimer = null;
- }
- },
- renderSlices(slices, force = false, interval = 0) {
- if (!interval) {
- slices.forEach(slice => slice.render(force));
- return;
- }
- const meta = this.metadata;
- const refreshTime = Math.max(interval, meta.stagger_time || 5000); // default 5 seconds
- if (typeof meta.stagger_refresh !== 'boolean') {
- meta.stagger_refresh = meta.stagger_refresh === undefined ?
- true : meta.stagger_refresh === 'true';
- }
- const delay = meta.stagger_refresh ? refreshTime / (slices.length - 1) : 0;
- slices.forEach((slice, i) => {
- setTimeout(() => slice.render(force), delay * i);
- });
- },
- startPeriodicRender(interval) {
- this.stopPeriodicRender();
- const dash = this;
- const immune = this.metadata.timed_refresh_immune_slices || [];
- const refreshAll = () => {
- const slices = dash.sliceObjects
- .filter(slice => immune.indexOf(slice.data.slice_id) === -1);
- dash.fetchSlices(slices, true, interval * 0.2);
- };
- const fetchAndRender = function () {
- refreshAll();
- if (interval > 0) {
- dash.refreshTimer = setTimeout(function () {
- fetchAndRender();
- }, interval);
- }
- };
- fetchAndRender();
- },
- refreshExcept(sliceId) {
- const immune = this.metadata.filter_immune_slices || [];
- const slices = this.sliceObjects.filter(slice =>
- slice.data.slice_id !== sliceId && immune.indexOf(slice.data.slice_id) === -1);
- this.renderSlices(slices);
- },
- clearFilters(sliceId) {
- delete this.filters[sliceId];
- this.refreshExcept(sliceId);
- this.updateFilterParamsInUrl();
- },
- removeFilter(sliceId, col, vals) {
- if (sliceId in this.filters) {
- if (col in this.filters[sliceId]) {
- const a = [];
- this.filters[sliceId][col].forEach(function (v) {
- if (vals.indexOf(v) < 0) {
- a.push(v);
- }
- });
- this.filters[sliceId][col] = a;
- }
- }
- this.refreshExcept(sliceId);
- this.updateFilterParamsInUrl();
- },
- getSlice(sliceId) {
- const id = parseInt(sliceId, 10);
- let i = 0;
- let slice = null;
- while (i < this.sliceObjects.length) {
- // when the slice is found, assign to slice and break;
- if (this.sliceObjects[i].data.slice_id === id) {
- slice = this.sliceObjects[i];
- break;
- }
- i++;
- }
- return slice;
- },
- getAjaxErrorMsg(error) {
- const respJSON = error.responseJSON;
- return (respJSON && respJSON.message) ? respJSON.message :
- error.responseText;
- },
- addSlicesToDashboard(sliceIds) {
- const getAjaxErrorMsg = this.getAjaxErrorMsg;
- $.ajax({
- type: 'POST',
- url: `/superset/add_slices/${dashboard.id}/`,
- data: {
- data: JSON.stringify({ slice_ids: sliceIds }),
- },
- success() {
- // Refresh page to allow for slices to re-render
- window.location.reload();
- },
- error(error) {
- const errorMsg = getAjaxErrorMsg(error);
- utils.showModal({
- title: t('Error'),
- body: t('Sorry, there was an error adding slices to this dashboard: %s', errorMsg),
- });
- },
- });
- },
- updateDashboardTitle(title) {
- this.dashboard_title = title;
- this.onChange();
- },
- });
-}
-
-$(document).ready(() => {
- // Getting bootstrapped data from the DOM
- utils.initJQueryAjax();
- const dashboardData = $('.dashboard').data('bootstrap');
-
- const state = getInitialState(dashboardData);
- px = superset(state);
- const dashboard = dashboardContainer(state.dashboard, state.datasources, state.userId);
- initDashboardView(dashboard);
- dashboard.init();
-});
diff --git a/superset/assets/javascripts/modules/superset.js b/superset/assets/javascripts/modules/superset.js
deleted file mode 100644
index 9d58f02..0000000
--- a/superset/assets/javascripts/modules/superset.js
+++ /dev/null
@@ -1,262 +0,0 @@
-/* eslint camel-case: 0 */
-import $ from 'jquery';
-import Mustache from 'mustache';
-import vizMap from '../../visualizations/main';
-import { getExploreUrl } from '../explore/exploreUtils';
-import { applyDefaultFormData } from '../explore/stores/store';
-import { t } from '../locales';
-
-const utils = require('./utils');
-
-/* eslint wrap-iife: 0 */
-const px = function (state) {
- let slice;
- const timeout = state.common.conf.SUPERSET_WEBSERVER_TIMEOUT;
- function getParam(name) {
- /* eslint no-useless-escape: 0 */
- const formattedName = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
- const regex = new RegExp('[\\?&]' + formattedName + '=([^&#]*)');
- const results = regex.exec(location.search);
- return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
- }
- function initFavStars() {
- const baseUrl = '/superset/favstar/';
- // Init star behavihor for favorite
- function show() {
- if ($(this).hasClass('selected')) {
- $(this).html('<i class="fa fa-star"></i>');
- } else {
- $(this).html('<i class="fa fa-star-o"></i>');
- }
- }
- $('.favstar')
- .attr('title', t('Click to favorite/unfavorite'))
- .css('cursor', 'pointer')
- .each(show)
- .each(function () {
- let url = baseUrl + $(this).attr('class_name');
- const star = this;
- url += '/' + $(this).attr('obj_id') + '/';
- $.getJSON(url + 'count/', function (data) {
- if (data.count > 0) {
- $(star).addClass('selected').each(show);
- }
- });
- })
- .click(function () {
- $(this).toggleClass('selected');
- let url = baseUrl + $(this).attr('class_name');
- url += '/' + $(this).attr('obj_id') + '/';
- if ($(this).hasClass('selected')) {
- url += 'select/';
- } else {
- url += 'unselect/';
- }
- $.get(url);
- $(this).each(show);
- })
- .tooltip();
- }
- const Slice = function (data, datasource, controller) {
- const token = $('#token_' + data.slice_id);
- const controls = $('#controls_' + data.slice_id);
- const containerId = 'con_' + data.slice_id;
- const selector = '#' + containerId;
- const container = $(selector);
- const sliceId = data.slice_id;
- const formData = applyDefaultFormData(data.form_data);
- const sliceCell = $(`#${data.slice_id}-cell`);
- slice = {
- data,
- formData,
- container,
- containerId,
- datasource,
- selector,
- getWidgetHeader() {
- return this.container.parents('div.widget').find('.chart-header');
- },
- render_template(s) {
- const context = {
- width: this.width,
- height: this.height,
- };
- return Mustache.render(s, context);
- },
- jsonEndpoint(data) {
- return this.endpoint(data, 'json');
- },
- endpoint(data, endpointType = 'json') {
- let endpoint = getExploreUrl(data, endpointType, this.force);
- if (endpoint.charAt(0) !== '/') {
- // Known issue for IE <= 11:
- // https://connect.microsoft.com/IE/feedbackdetail/view/1002846/pathname-incorrect-for-out-of-document-elements
- endpoint = '/' + endpoint;
- }
- return endpoint;
- },
- d3format(col, number) {
- // uses the utils memoized d3format function and formats based on
- // column level defined preferences
- let format = '.3s';
- if (this.datasource.column_formats[col]) {
- format = this.datasource.column_formats[col];
- }
- return utils.d3format(format, number);
- },
- /* eslint no-shadow: 0 */
- always(data) {
- if (data && data.query) {
- slice.viewSqlQuery = data.query;
- }
- },
- done(payload) {
- Object.assign(data, payload);
-
- token.find('img.loading').hide();
- container.fadeTo(0.5, 1);
- sliceCell.removeClass('slice-cell-highlight');
- container.show();
-
- $('.query-and-save button').removeAttr('disabled');
- this.always(data);
- controller.done(this);
- },
- getErrorMsg(xhr) {
- let msg = '';
- if (!xhr.responseText) {
- const status = xhr.status;
- if (status === 0) {
- // This may happen when the worker in gunicorn times out
- msg += (
- t('The server could not be reached. You may want to ' +
- 'verify your connection and try again.'));
- } else {
- msg += (t('An unknown error occurred. (Status: %s )', status));
- }
- }
- return msg;
- },
- error(msg, xhr) {
- let errorMsg = msg;
- token.find('img.loading').hide();
- container.fadeTo(0.5, 1);
- sliceCell.removeClass('slice-cell-highlight');
- let errHtml = '';
- let o;
- try {
- o = JSON.parse(msg);
- if (o.error) {
- errorMsg = o.error;
- }
- } catch (e) {
- // pass
- }
- if (errorMsg) {
- errHtml += `<div class="alert alert-danger">${errorMsg}</div>`;
- }
- if (xhr) {
- if (xhr.statusText === 'timeout') {
- errHtml += (
- '<div class="alert alert-warning">' +
- 'Query timeout - visualization query are set to time out ' +
- `at ${timeout} seconds.</div>`);
- } else {
- const extendedMsg = this.getErrorMsg(xhr);
- if (extendedMsg) {
- errHtml += `<div class="alert alert-danger">${extendedMsg}</div>`;
- }
- }
- }
- container.html(errHtml);
- container.show();
- $('span.query').removeClass('disabled');
- $('.query-and-save button').removeAttr('disabled');
- this.always(o);
- controller.error(this);
- },
- clearError() {
- $(selector + ' div.alert').remove();
- },
- width() {
- return container.width();
- },
- height() {
- let others = 0;
- const widget = container.parents('.widget');
- const sliceDescription = widget.find('.slice_description');
- if (sliceDescription.is(':visible')) {
- others += widget.find('.slice_description').height() + 25;
- }
- others += widget.find('.chart-header').height();
- return widget.height() - others - 10;
- },
- bindResizeToWindowResize() {
- let resizeTimer;
- const slice = this;
- $(window).on('resize', function () {
- clearTimeout(resizeTimer);
- resizeTimer = setTimeout(function () {
- slice.resize();
- }, 500);
- });
- },
- render(force) {
- if (force === undefined) {
- this.force = false;
- } else {
- this.force = force;
- }
- const formDataExtra = Object.assign({}, formData);
- formDataExtra.extra_filters = controller.effectiveExtraFilters(sliceId);
- controls.find('a.exploreChart').attr('href', getExploreUrl(formDataExtra));
- controls.find('a.exportCSV').attr('href', getExploreUrl(formDataExtra, 'csv'));
- token.find('img.loading').show();
- container.fadeTo(0.5, 0.25);
- sliceCell.addClass('slice-cell-highlight');
- container.css('height', this.height());
- $.ajax({
- url: this.jsonEndpoint(formDataExtra),
- timeout: timeout * 1000,
- success: (queryResponse) => {
- try {
- vizMap[formData.viz_type](this, queryResponse);
- this.done(queryResponse);
- } catch (e) {
- this.error(t('An error occurred while rendering the visualization: %s', e));
- }
- },
- error: (err) => {
- this.error(err.responseText, err);
- },
- });
- },
- resize() {
- this.render();
- },
- addFilter(col, vals, merge = true, refresh = true) {
- controller.addFilter(sliceId, col, vals, merge, refresh);
- },
- setFilter(col, vals, refresh = true) {
- controller.setFilter(sliceId, col, vals, refresh);
- },
- getFilters() {
- return controller.filters[sliceId];
- },
- clearFilter() {
- controller.clearFilter(sliceId);
- },
- removeFilter(col, vals) {
- controller.removeFilter(sliceId, col, vals);
- },
- };
- return slice;
- };
- // Export public functions
- return {
- getParam,
- initFavStars,
- Slice,
- };
-};
-module.exports = px;
--
To stop receiving notification emails like this one, please contact
['"commits@superset.apache.org" <co...@superset.apache.org>'].