superset-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ccwilli...@apache.org
Subject [incubator-superset] 04/13: [superset-client] replace all chart ajax calls with SupersetClient
Date Mon, 15 Oct 2018 19:52:13 GMT
This is an automated email from the ASF dual-hosted git repository.

ccwilliams pushed a commit to branch chris--ajax-charts
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git

commit e0ba5f24b7311a92f984e371852046a5f1c1b52e
Author: Chris Williams <chris.williams@airbnb.com>
AuthorDate: Wed Sep 12 11:42:47 2018 -0700

    [superset-client] replace all chart ajax calls with SupersetClient
---
 superset/assets/src/chart/Chart.jsx                |  25 +-
 superset/assets/src/chart/chartAction.js           |  99 ++++---
 .../src/explore/components/ExploreChartPanel.jsx   |   2 +-
 .../explore/components/ExploreViewContainer.jsx    |  15 +-
 .../components/controls/AnnotationLayer.jsx        | 287 ++++++++++-----------
 .../assets/src/explore/reducers/getInitialState.js |   9 +-
 6 files changed, 231 insertions(+), 206 deletions(-)

diff --git a/superset/assets/src/chart/Chart.jsx b/superset/assets/src/chart/Chart.jsx
index 0d55027..80d2513 100644
--- a/superset/assets/src/chart/Chart.jsx
+++ b/superset/assets/src/chart/Chart.jsx
@@ -1,8 +1,10 @@
 import React from 'react';
 import PropTypes from 'prop-types';
+import Mustache from 'mustache';
 import { Tooltip } from 'react-bootstrap';
 import dompurify from 'dompurify';
 
+import { d3format } from '../modules/utils';
 import ChartBody from './ChartBody';
 import Loading from '../components/Loading';
 import { Logger, LOG_ACTIONS_RENDER_CHART } from '../logger';
@@ -31,7 +33,6 @@ const propTypes = {
   chartUpdateEndTime: PropTypes.number,
   chartUpdateStartTime: PropTypes.number,
   latestQueryFormData: PropTypes.object,
-  queryRequest: PropTypes.object,
   queryResponse: PropTypes.object,
   lastRendered: PropTypes.number,
   triggerQuery: PropTypes.bool,
@@ -166,10 +167,30 @@ class Chart extends React.PureComponent {
     );
   }
 
+  d3format(col, number) {
+    const { datasource } = this.props;
+    const format = (datasource.column_formats && datasource.column_formats[col])
|| '0.3s';
+
+    return d3format(format, number);
+  }
+
   error(e) {
     this.props.actions.chartRenderingFailed(e, this.props.chartId);
   }
 
+  verboseMetricName(metric) {
+    return this.props.datasource.verbose_map[metric] || metric;
+  }
+
+  // eslint-disable-next-line camelcase
+  render_template(s) {
+    const context = {
+      width: this.width(),
+      height: this.height(),
+    };
+    return Mustache.render(s, context);
+  }
+
   renderTooltip() {
     if (this.state.tooltip) {
       return (
@@ -177,7 +198,7 @@ class Chart extends React.PureComponent {
           className="chart-tooltip"
           id="chart-tooltip"
           placement="right"
-          positionTop={this.state.tooltip.y + 30}
+          positionTop={this.state.tooltip.y - 10}
           positionLeft={this.state.tooltip.x + 30}
           arrowOffsetTop={10}
         >
diff --git a/superset/assets/src/chart/chartAction.js b/superset/assets/src/chart/chartAction.js
index 8adbdc0..cb835c5 100644
--- a/superset/assets/src/chart/chartAction.js
+++ b/superset/assets/src/chart/chartAction.js
@@ -1,16 +1,16 @@
-import URI from 'urijs';
-
+/* global window, AbortController */
+/* eslint no-undef: 'error' */
+import { SupersetClient } from '@superset-ui/core';
 import { getExploreUrlAndPayload, getAnnotationJsonUrl } from '../explore/exploreUtils';
 import { requiresQuery, ANNOTATION_SOURCE_TYPES } from '../modules/AnnotationTypes';
+import { addDangerToast } from '../messageToasts/actions';
 import { Logger, LOG_ACTIONS_LOAD_CHART } from '../logger';
 import { COMMON_ERR_MESSAGES } from '../utils/common';
 import { t } from '../locales';
 
-const $ = (window.$ = require('jquery'));
-
 export const CHART_UPDATE_STARTED = 'CHART_UPDATE_STARTED';
-export function chartUpdateStarted(queryRequest, latestQueryFormData, key) {
-  return { type: CHART_UPDATE_STARTED, queryRequest, latestQueryFormData, key };
+export function chartUpdateStarted(queryController, latestQueryFormData, key) {
+  return { type: CHART_UPDATE_STARTED, queryController, latestQueryFormData, key };
 }
 
 export const CHART_UPDATE_SUCCEEDED = 'CHART_UPDATE_SUCCEEDED';
@@ -54,8 +54,8 @@ export function annotationQuerySuccess(annotation, queryResponse, key) {
 }
 
 export const ANNOTATION_QUERY_STARTED = 'ANNOTATION_QUERY_STARTED';
-export function annotationQueryStarted(annotation, queryRequest, key) {
-  return { type: ANNOTATION_QUERY_STARTED, annotation, queryRequest, key };
+export function annotationQueryStarted(annotation, queryController, key) {
+  return { type: ANNOTATION_QUERY_STARTED, annotation, queryController, key };
 }
 
 export const ANNOTATION_QUERY_FAILED = 'ANNOTATION_QUERY_FAILED';
@@ -85,18 +85,21 @@ export function runAnnotationQuery(annotation, timeout = 60, formData
= null, ke
     );
     const isNative = annotation.sourceType === ANNOTATION_SOURCE_TYPES.NATIVE;
     const url = getAnnotationJsonUrl(annotation.value, sliceFormData, isNative);
-    const queryRequest = $.ajax({
+    const controller = new AbortController();
+    const { signal } = controller;
+
+    dispatch(annotationQueryStarted(annotation, controller, sliceKey));
+
+    return SupersetClient.get({
       url,
-      dataType: 'json',
+      signal,
       timeout: timeout * 1000,
-    });
-    dispatch(annotationQueryStarted(annotation, queryRequest, sliceKey));
-    return queryRequest
-      .then(queryResponse => dispatch(annotationQuerySuccess(annotation, queryResponse,
sliceKey)))
+    })
+      .then(({ json }) => dispatch(annotationQuerySuccess(annotation, json, sliceKey)))
       .catch((err) => {
         if (err.statusText === 'timeout') {
           dispatch(annotationQueryFailed(annotation, { error: 'Query Timeout' }, sliceKey));
-        } else if ((err.responseJSON.error || '').toLowerCase().startsWith('no data')) {
+        } else if ((err.responseJSON.error || '').toLowerCase().includes('no data')) {
           dispatch(annotationQuerySuccess(annotation, err, sliceKey));
         } else if (err.statusText !== 'abort') {
           dispatch(annotationQueryFailed(annotation, err.responseJSON, sliceKey));
@@ -135,30 +138,30 @@ export function runQuery(formData, force = false, timeout = 60, key)
{
       force,
     });
     const logStart = Logger.getTimestamp();
-    const queryRequest = $.ajax({
-      type: 'POST',
+    const controller = new AbortController();
+    const { signal } = controller;
+
+    dispatch(chartUpdateStarted(controller, payload, key));
+
+    const queryPromise = SupersetClient.post({
       url,
-      dataType: 'json',
-      data: {
-        form_data: JSON.stringify(payload),
-      },
+      postPayload: { form_data: payload },
+      signal,
       timeout: timeout * 1000,
-    });
-    const queryPromise = Promise.resolve(dispatch(chartUpdateStarted(queryRequest, payload,
key)))
-      .then(() => queryRequest)
-      .then((queryResponse) => {
+    })
+      .then(({ json }) => {
         Logger.append(LOG_ACTIONS_LOAD_CHART, {
           slice_id: key,
-          is_cached: queryResponse.is_cached,
+          is_cached: json.is_cached,
           force_refresh: force,
-          row_count: queryResponse.rowcount,
+          row_count: json.rowcount,
           datasource: formData.datasource,
           start_offset: logStart,
           duration: Logger.getTimestamp() - logStart,
           has_extra_filters: formData.extra_filters && formData.extra_filters.length
> 0,
           viz_type: formData.viz_type,
         });
-        return dispatch(chartUpdateSucceeded(queryResponse, key));
+        return dispatch(chartUpdateSucceeded(json, key));
       })
       .catch((err) => {
         Logger.append(LOG_ACTIONS_LOAD_CHART, {
@@ -170,7 +173,7 @@ export function runQuery(formData, force = false, timeout = 60, key) {
         });
         if (err.statusText === 'timeout') {
           dispatch(chartUpdateTimeout(err.statusText, timeout, key));
-        } else if (err.statusText === 'abort') {
+        } else if (err.statusText === 'AbortError') {
           dispatch(chartUpdateStopped(key));
         } else {
           let errObject;
@@ -193,7 +196,9 @@ export function runQuery(formData, force = false, timeout = 60, key) {
           dispatch(chartUpdateFailed(errObject, key));
         }
       });
+
     const annotationLayers = formData.annotation_layers || [];
+
     return Promise.all([
       queryPromise,
       dispatch(triggerQuery(false, key)),
@@ -203,29 +208,21 @@ export function runQuery(formData, force = false, timeout = 60, key)
{
   };
 }
 
-export const SQLLAB_REDIRECT_FAILED = 'SQLLAB_REDIRECT_FAILED';
-export function sqllabRedirectFailed(error, key) {
-  return { type: SQLLAB_REDIRECT_FAILED, error, key };
-}
-
 export function redirectSQLLab(formData) {
-  return function (dispatch) {
-    const { url, payload } = getExploreUrlAndPayload({ formData, endpointType: 'query' });
-    $.ajax({
-      type: 'POST',
-      url,
-      data: {
-        form_data: JSON.stringify(payload),
-      },
-      success: (response) => {
-        const redirectUrl = new URI(window.location);
-        redirectUrl
-          .pathname('/superset/sqllab')
-          .search({ datasourceKey: formData.datasource, sql: response.query });
-        window.open(redirectUrl.href(), '_blank');
-      },
-      error: (xhr, status, error) => dispatch(sqllabRedirectFailed(error, formData.slice_id)),
-    });
+  return (dispatch) => {
+    const { url } = getExploreUrlAndPayload({ formData, endpointType: 'query' });
+    return SupersetClient.get({ url })
+      .then(({ json }) => {
+        const redirectUrl = new URL(window.location);
+        redirectUrl.pathname = '/superset/sqllab';
+        for (const key of redirectUrl.searchParams.keys()) {
+          redirectUrl.searchParams.delete(key);
+        }
+        redirectUrl.searchParams.set('datasourceKey', formData.datasource);
+        redirectUrl.searchParams.set('sql', json.query);
+        window.open(redirectUrl.href, '_blank');
+      })
+      .catch(() => dispatch(addDangerToast(t('An error occurred while loading the SQL'))));
   };
 }
 
diff --git a/superset/assets/src/explore/components/ExploreChartPanel.jsx b/superset/assets/src/explore/components/ExploreChartPanel.jsx
index bcda75d..9d30284 100644
--- a/superset/assets/src/explore/components/ExploreChartPanel.jsx
+++ b/superset/assets/src/explore/components/ExploreChartPanel.jsx
@@ -62,7 +62,7 @@ class ExploreChartPanel extends React.PureComponent {
         latestQueryFormData={chart.latestQueryFormData}
         lastRendered={chart.lastRendered}
         queryResponse={chart.queryResponse}
-        queryRequest={chart.queryRequest}
+        queryController={chart.queryController}
         triggerQuery={chart.triggerQuery}
       />
     );
diff --git a/superset/assets/src/explore/components/ExploreViewContainer.jsx b/superset/assets/src/explore/components/ExploreViewContainer.jsx
index 34f165d..fc95cef 100644
--- a/superset/assets/src/explore/components/ExploreViewContainer.jsx
+++ b/superset/assets/src/explore/components/ExploreViewContainer.jsx
@@ -54,6 +54,9 @@ class ExploreViewContainer extends React.Component {
     this.addHistory = this.addHistory.bind(this);
     this.handleResize = this.handleResize.bind(this);
     this.handlePopstate = this.handlePopstate.bind(this);
+    this.onStop = this.onStop.bind(this);
+    this.onQuery = this.onQuery.bind(this);
+    this.toggleModal = this.toggleModal.bind(this);
   }
 
   componentDidMount() {
@@ -124,7 +127,9 @@ class ExploreViewContainer extends React.Component {
   }
 
   onStop() {
-    return this.props.chart.queryRequest.abort();
+    if (this.props.chart && this.props.chart.queryController) {
+      this.props.chart.queryController.abort();
+    }
   }
 
   getWidth() {
@@ -262,7 +267,7 @@ class ExploreViewContainer extends React.Component {
       >
         {this.state.showModal && (
           <SaveModal
-            onHide={this.toggleModal.bind(this)}
+            onHide={this.toggleModal}
             actions={this.props.actions}
             form_data={this.props.form_data}
           />
@@ -271,9 +276,9 @@ class ExploreViewContainer extends React.Component {
           <div className="col-sm-4">
             <QueryAndSaveBtns
               canAdd="True"
-              onQuery={this.onQuery.bind(this)}
-              onSave={this.toggleModal.bind(this)}
-              onStop={this.onStop.bind(this)}
+              onQuery={this.onQuery}
+              onSave={this.toggleModal}
+              onStop={this.onStop}
               loading={this.props.chart.chartStatus === 'loading'}
               chartIsStale={this.state.chartIsStale}
               errorMessage={this.renderErrorMessage()}
diff --git a/superset/assets/src/explore/components/controls/AnnotationLayer.jsx b/superset/assets/src/explore/components/controls/AnnotationLayer.jsx
index d37743c..cbf17a1 100644
--- a/superset/assets/src/explore/components/controls/AnnotationLayer.jsx
+++ b/superset/assets/src/explore/components/controls/AnnotationLayer.jsx
@@ -2,10 +2,9 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import { CompactPicker } from 'react-color';
 import { Button } from 'react-bootstrap';
-
-import $ from 'jquery';
 import mathjs from 'mathjs';
 
+import { SupersetClient } from '@superset-ui/core';
 import SelectControl from './SelectControl';
 import TextControl from './TextControl';
 import CheckboxControl from './CheckboxControl';
@@ -83,10 +82,24 @@ const defaultProps = {
 export default class AnnotationLayer extends React.PureComponent {
   constructor(props) {
     super(props);
-    const { name, annotationType, sourceType,
-      color, opacity, style, width, showMarkers, hideLine, value,
-      overrides, show, titleColumn, descriptionColumns,
-      timeColumn, intervalEndColumn } = props;
+    const {
+      name,
+      annotationType,
+      sourceType,
+      color,
+      opacity,
+      style,
+      width,
+      showMarkers,
+      hideLine,
+      value,
+      overrides,
+      show,
+      titleColumn,
+      descriptionColumns,
+      timeColumn,
+      intervalEndColumn,
+    } = props;
     this.state = {
       // base
       name,
@@ -119,8 +132,7 @@ export default class AnnotationLayer extends React.PureComponent {
     this.applyAnnotation = this.applyAnnotation.bind(this);
     this.fetchOptions = this.fetchOptions.bind(this);
     this.handleAnnotationType = this.handleAnnotationType.bind(this);
-    this.handleAnnotationSourceType =
-        this.handleAnnotationSourceType.bind(this);
+    this.handleAnnotationSourceType = this.handleAnnotationSourceType.bind(this);
     this.handleValue = this.handleValue.bind(this);
     this.isValidForm = this.isValidForm.bind(this);
   }
@@ -139,7 +151,10 @@ export default class AnnotationLayer extends React.PureComponent {
   isValidFormula(value, annotationType) {
     if (annotationType === AnnotationTypes.FORMULA) {
       try {
-        mathjs.parse(value).compile().eval({ x: 0 });
+        mathjs
+          .parse(value)
+          .compile()
+          .eval({ x: 0 });
       } catch (err) {
         return true;
       }
@@ -148,10 +163,7 @@ export default class AnnotationLayer extends React.PureComponent {
   }
 
   isValidForm() {
-    const {
-      name, annotationType, sourceType,
-      value, timeColumn, intervalEndColumn,
-    } = this.state;
+    const { name, annotationType, sourceType, value, timeColumn, intervalEndColumn } = this.state;
     const errors = [nonEmpty(name), nonEmpty(annotationType), nonEmpty(value)];
     if (sourceType !== ANNOTATION_SOURCE_TYPES.NATIVE) {
       if (annotationType === AnnotationTypes.EVENT) {
@@ -166,7 +178,6 @@ export default class AnnotationLayer extends React.PureComponent {
     return !errors.filter(x => x).length;
   }
 
-
   handleAnnotationType(annotationType) {
     this.setState({
       annotationType,
@@ -199,31 +210,25 @@ export default class AnnotationLayer extends React.PureComponent {
   fetchOptions(annotationType, sourceType, isLoadingOptions) {
     if (isLoadingOptions === true) {
       if (sourceType === ANNOTATION_SOURCE_TYPES.NATIVE) {
-        $.ajax({
-          type: 'GET',
-          url: '/annotationlayermodelview/api/read?',
-        }).then((data) => {
-          const layers = data ? data.result.map(layer => ({
-            value: layer.id,
-            label: layer.name,
-          })) : [];
+        SupersetClient.get({ endpoint: '/annotationlayermodelview/api/read?' }).then(({ json
}) => {
+          const layers = json
+            ? json.result.map(layer => ({
+                value: layer.id,
+                label: layer.name,
+              }))
+            : [];
           this.setState({
             isLoadingOptions: false,
             valueOptions: layers,
           });
         });
       } else if (requiresQuery(sourceType)) {
-        $.ajax({
-          type: 'GET',
-          url: '/superset/user_slices',
-        }).then(data =>
+        SupersetClient.get({ endpoint: '/superset/user_slices' }).then(({ json }) =>
           this.setState({
             isLoadingOptions: false,
-            valueOptions: data.filter(
-                x => getSupportedSourceTypes(annotationType)
-                .find(v => v === x.viz_type))
-                .map(x => ({ value: x.id, label: x.title, slice: x }),
-              ),
+            valueOptions: json
+              .filter(x => getSupportedSourceTypes(annotationType).find(v => v ===
x.viz_type))
+              .map(x => ({ value: x.id, label: x.title, slice: x })),
           }),
         );
       } else {
@@ -266,26 +271,26 @@ export default class AnnotationLayer extends React.PureComponent {
   }
 
   renderValueConfiguration() {
-    const { annotationType, sourceType, value,
-      valueOptions, isLoadingOptions } = this.state;
+    const { annotationType, sourceType, value, valueOptions, isLoadingOptions } = this.state;
     let label = '';
     let description = '';
     if (requiresQuery(sourceType)) {
       if (sourceType === ANNOTATION_SOURCE_TYPES.NATIVE) {
-        label = t('Annotation Layer');
-        description = t('Select the Annotation Layer you would like to use.');
+        label = 'Annotation Layer';
+        description = 'Select the Annotation Layer you would like to use.';
       } else {
-        label = t('Chart');
+        label = label = t('Chart');
         description = `Use a pre defined Superset Chart as a source for annotations and overlays.
-        'your chart must be one of these visualization types:
-        '[${getSupportedSourceTypes(annotationType)
-            .map(x => ((x in vizTypes && 'label' in vizTypes[x]) ? vizTypes[x].label
: '')).join(', ')}]'`;
+        your chart must be one of these visualization types:
+        [${getSupportedSourceTypes(annotationType)
+          .map(x => (x in vizTypes && 'label' in vizTypes[x] ? vizTypes[x].label
: ''))
+          .join(', ')}]`;
       }
     } else if (annotationType === AnnotationTypes.FORMULA) {
-      label = t('Formula');
-      description = t(`Expects a formula with depending time parameter 'x'
+      label = 'Formula';
+      description = `Expects a formula with depending time parameter 'x'
         in milliseconds since epoch. mathjs is used to evaluate the formulas.
-        Example: '2x+5'`);
+        Example: '2x+5'`;
     }
     if (requiresQuery(sourceType)) {
       return (
@@ -300,10 +305,11 @@ export default class AnnotationLayer extends React.PureComponent {
           isLoading={isLoadingOptions}
           value={value}
           onChange={this.handleValue}
-          validationErrors={!value ? [t('Mandatory')] : []}
+          validationErrors={!value ? ['Mandatory'] : []}
         />
       );
-    } if (annotationType === AnnotationTypes.FORMULA) {
+    }
+    if (annotationType === AnnotationTypes.FORMULA) {
       return (
         <TextControl
           name="annotation-layer-value"
@@ -314,7 +320,7 @@ export default class AnnotationLayer extends React.PureComponent {
           placeholder=""
           value={value}
           onChange={this.handleValue}
-          validationErrors={this.isValidFormula(value, annotationType) ? [t('Bad formula.')]
: []}
+          validationErrors={this.isValidFormula(value, annotationType) ? ['Bad formula.']
: []}
         />
       );
     }
@@ -322,37 +328,43 @@ export default class AnnotationLayer extends React.PureComponent {
   }
 
   renderSliceConfiguration() {
-    const { annotationType, sourceType, value, valueOptions, overrides, titleColumn,
-      timeColumn, intervalEndColumn, descriptionColumns } = this.state;
+    const {
+      annotationType,
+      sourceType,
+      value,
+      valueOptions,
+      overrides,
+      titleColumn,
+      timeColumn,
+      intervalEndColumn,
+      descriptionColumns,
+    } = this.state;
     const slice = (valueOptions.find(x => x.value === value) || {}).slice;
     if (sourceType !== ANNOTATION_SOURCE_TYPES.NATIVE && slice) {
-      const columns = (slice.data.groupby || []).concat(
-        (slice.data.all_columns || [])).map(x => ({ value: x, label: x }));
-      const timeColumnOptions = slice.data.include_time ?
-        [{ value: '__timestamp', label: '__timestamp' }].concat(columns) : columns;
+      const columns = (slice.data.groupby || [])
+        .concat(slice.data.all_columns || [])
+        .map(x => ({ value: x, label: x }));
+      const timeColumnOptions = slice.data.include_time
+        ? [{ value: '__timestamp', label: '__timestamp' }].concat(columns)
+        : columns;
       return (
         <div style={{ marginRight: '2rem' }}>
           <PopoverSection
             isSelected
-            onSelect={() => {
-            }}
+            onSelect={() => {}}
             title="Annotation Slice Configuration"
-            info={
-              `This section allows you to configure how to use the slice
-               to generate annotations.`
-            }
+            info={`This section allows you to configure how to use the slice
+               to generate annotations.`}
           >
-            {
-              (
-                annotationType === AnnotationTypes.EVENT ||
-                annotationType === AnnotationTypes.INTERVAL
-              ) &&
+            {(annotationType === AnnotationTypes.EVENT ||
+              annotationType === AnnotationTypes.INTERVAL) && (
               <SelectControl
                 hovered
                 name="annotation-layer-time-column"
                 label={
-                  annotationType === AnnotationTypes.INTERVAL ?
-                    'Interval Start column' : 'Event Time Column'
+                  annotationType === AnnotationTypes.INTERVAL
+                    ? 'Interval Start column'
+                    : 'Event Time Column'
                 }
                 description={'This column must contain date/time information.'}
                 validationErrors={!timeColumn ? ['Mandatory'] : []}
@@ -361,9 +373,8 @@ export default class AnnotationLayer extends React.PureComponent {
                 value={timeColumn}
                 onChange={v => this.setState({ timeColumn: v })}
               />
-            }
-            {
-              annotationType === AnnotationTypes.INTERVAL &&
+            )}
+            {annotationType === AnnotationTypes.INTERVAL && (
               <SelectControl
                 hovered
                 name="annotation-layer-intervalEnd"
@@ -374,20 +385,17 @@ export default class AnnotationLayer extends React.PureComponent {
                 value={intervalEndColumn}
                 onChange={v => this.setState({ intervalEndColumn: v })}
               />
-            }
+            )}
             <SelectControl
               hovered
               name="annotation-layer-title"
               label="Title Column"
               description={'Pick a title for you annotation.'}
-              options={
-                [{ value: '', label: 'None' }].concat(columns)
-              }
+              options={[{ value: '', label: 'None' }].concat(columns)}
               value={titleColumn}
               onChange={v => this.setState({ titleColumn: v })}
             />
-            {
-              annotationType !== AnnotationTypes.TIME_SERIES &&
+            {annotationType !== AnnotationTypes.TIME_SERIES && (
               <SelectControl
                 hovered
                 name="annotation-layer-title"
@@ -395,13 +403,11 @@ export default class AnnotationLayer extends React.PureComponent {
                 description={`Pick one or more columns that should be shown in the
                   annotation. If you don't select a column all of them will be shown.`}
                 multi
-                options={
-                  columns
-                }
+                options={columns}
                 value={descriptionColumns}
                 onChange={v => this.setState({ descriptionColumns: v })}
               />
-            }
+            )}
             <div style={{ marginTop: '1rem' }}>
               <CheckboxControl
                 hovered
@@ -473,14 +479,17 @@ export default class AnnotationLayer extends React.PureComponent {
         </div>
       );
     }
-    return ('');
+    return '';
   }
 
   renderDisplayConfiguration() {
     const { color, opacity, style, width, showMarkers, hideLine, annotationType } = this.state;
     const colorScheme = [...getScheme(this.props.colorScheme)];
-    if (color && color !== AUTOMATIC_COLOR &&
-      !colorScheme.find(x => x.toLowerCase() === color.toLowerCase())) {
+    if (
+      color &&
+      color !== AUTOMATIC_COLOR &&
+      !colorScheme.find(x => x.toLowerCase() === color.toLowerCase())
+    ) {
       colorScheme.push(color);
     }
     return (
@@ -493,12 +502,12 @@ export default class AnnotationLayer extends React.PureComponent {
         <SelectControl
           name="annotation-layer-stroke"
           label={t('Style')}
-            // see '../../../visualizations/nvd3_vis.css'
+          // see '../../../visualizations/nvd3_vis.css'
           options={[
-              { value: 'solid', label: 'Solid' },
-              { value: 'dashed', label: 'Dashed' },
-              { value: 'longDashed', label: 'Long Dashed' },
-              { value: 'dotted', label: 'Dotted' },
+            { value: 'solid', label: 'Solid' },
+            { value: 'dashed', label: 'Dashed' },
+            { value: 'longDashed', label: 'Long Dashed' },
+            { value: 'dotted', label: 'Dotted' },
           ]}
           value={style}
           onChange={v => this.setState({ style: v })}
@@ -506,12 +515,12 @@ export default class AnnotationLayer extends React.PureComponent {
         <SelectControl
           name="annotation-layer-opacity"
           label={t('Opacity')}
-            // see '../../../visualizations/nvd3_vis.css'
+          // see '../../../visualizations/nvd3_vis.css'
           options={[
-              { value: '', label: 'Solid' },
-              { value: 'opacityLow', label: '0.2' },
-              { value: 'opacityMedium', label: '0.5' },
-              { value: 'opacityHigh', label: '0.8' },
+            { value: '', label: 'Solid' },
+            { value: 'opacityLow', label: '0.2' },
+            { value: 'opacityMedium', label: '0.5' },
+            { value: 'opacityHigh', label: '0.8' },
           ]}
           value={opacity}
           onChange={v => this.setState({ opacity: v })}
@@ -530,7 +539,7 @@ export default class AnnotationLayer extends React.PureComponent {
               bsSize="xsmall"
               onClick={() => this.setState({ color: AUTOMATIC_COLOR })}
             >
-              {t('Automatic Color')}
+              Automatic Color
             </Button>
           </div>
         </div>
@@ -541,42 +550,36 @@ export default class AnnotationLayer extends React.PureComponent {
           value={width}
           onChange={v => this.setState({ width: v })}
         />
-        {annotationType === AnnotationTypes.TIME_SERIES &&
-        <CheckboxControl
-          hovered
-          name="annotation-layer-show-markers"
-          label={t('Show Markers')}
-          description={t('Shows or hides markers for the time series')}
-          value={showMarkers}
-          onChange={v => this.setState({ showMarkers: v })}
-        />
-        }
-        {annotationType === AnnotationTypes.TIME_SERIES &&
-        <CheckboxControl
-          hovered
-          name="annotation-layer-hide-line"
-          label={t('Hide Line')}
-          description={t('Hides the Line for the time series')}
-          value={hideLine}
-          onChange={v => this.setState({ hideLine: v })}
-        />
-        }
+        {annotationType === AnnotationTypes.TIME_SERIES && (
+          <CheckboxControl
+            hovered
+            name="annotation-layer-show-markers"
+            label="Show Markers"
+            description={'Shows or hides markers for the time series'}
+            value={showMarkers}
+            onChange={v => this.setState({ showMarkers: v })}
+          />
+        )}
+        {annotationType === AnnotationTypes.TIME_SERIES && (
+          <CheckboxControl
+            hovered
+            name="annotation-layer-hide-line"
+            label="Hide Line"
+            description={'Hides the Line for the time series'}
+            value={hideLine}
+            onChange={v => this.setState({ hideLine: v })}
+          />
+        )}
       </PopoverSection>
     );
   }
 
   render() {
-    const { isNew, name, annotationType,
-      sourceType, show } = this.state;
+    const { isNew, name, annotationType, sourceType, show } = this.state;
     const isValid = this.isValidForm();
     return (
       <div>
-        {
-          this.props.error &&
-          <span style={{ color: 'red' }}>
-            ERROR: {this.props.error}
-          </span>
-        }
+        {this.props.error && <span style={{ color: 'red' }}>ERROR: {this.props.error}</span>}
         <div style={{ display: 'flex', flexDirection: 'row' }}>
           <div style={{ marginRight: '2rem' }}>
             <PopoverSection
@@ -604,50 +607,43 @@ export default class AnnotationLayer extends React.PureComponent {
                 description={t('Choose the Annotation Layer Type')}
                 label={t('Annotation Layer Type')}
                 name="annotation-layer-type"
-                options={getSupportedAnnotationTypes(this.props.vizType).map(
-                    x => ({ value: x, label: getAnnotationTypeLabel(x) }))}
+                options={getSupportedAnnotationTypes(this.props.vizType).map(x => ({
+                  value: x,
+                  label: getAnnotationTypeLabel(x),
+                }))}
                 value={annotationType}
                 onChange={this.handleAnnotationType}
               />
-              {!!getSupportedSourceTypes(annotationType).length &&
+              {!!getSupportedSourceTypes(annotationType).length && (
                 <SelectControl
                   hovered
-                  description={t('Choose the source of your annotations')}
-                  label={t('Annotation Source')}
+                  description="Choose the source of your annotations"
+                  label="Annotation Source"
                   name="annotation-source-type"
-                  options={getSupportedSourceTypes(annotationType).map(
-                        x => ({ value: x, label: getAnnotationSourceTypeLabels(x) }))}
+                  options={getSupportedSourceTypes(annotationType).map(x => ({
+                    value: x,
+                    label: getAnnotationSourceTypeLabels(x),
+                  }))}
                   value={sourceType}
                   onChange={this.handleAnnotationSourceType}
                 />
-              }
-              { this.renderValueConfiguration() }
+              )}
+              {this.renderValueConfiguration()}
             </PopoverSection>
           </div>
-          { this.renderSliceConfiguration() }
-          { this.renderDisplayConfiguration() }
+          {this.renderSliceConfiguration()}
+          {this.renderDisplayConfiguration()}
         </div>
         <div style={{ display: 'flex', justifyContent: 'space-between' }}>
-          <Button
-            bsSize="sm"
-            onClick={this.deleteAnnotation}
-          >
-            { !isNew ? t('Remove') : t('Cancel') }
+          <Button bsSize="sm" onClick={this.deleteAnnotation}>
+            {!isNew ? t('Remove') : t('Cancel')}
           </Button>
           <div>
-            <Button
-              bsSize="sm"
-              disabled={!isValid}
-              onClick={this.applyAnnotation}
-            >
+            <Button bsSize="sm" disabled={!isValid} onClick={this.applyAnnotation}>
               {t('Apply')}
             </Button>
 
-            <Button
-              bsSize="sm"
-              disabled={!isValid}
-              onClick={this.submitAnnotation}
-            >
+            <Button bsSize="sm" disabled={!isValid} onClick={this.submitAnnotation}>
               {t('OK')}
             </Button>
           </div>
@@ -656,5 +652,6 @@ export default class AnnotationLayer extends React.PureComponent {
     );
   }
 }
+
 AnnotationLayer.propTypes = propTypes;
 AnnotationLayer.defaultProps = defaultProps;
diff --git a/superset/assets/src/explore/reducers/getInitialState.js b/superset/assets/src/explore/reducers/getInitialState.js
index 28910bf..dec455c 100644
--- a/superset/assets/src/explore/reducers/getInitialState.js
+++ b/superset/assets/src/explore/reducers/getInitialState.js
@@ -5,9 +5,10 @@ import { now } from '../../modules/dates';
 import { getChartKey } from '../exploreUtils';
 import { getControlsState, getFormDataFromControls } from '../store';
 
-export default function (bootstrapData) {
+export default function getInitialState(bootstrapData) {
   const controls = getControlsState(bootstrapData, bootstrapData.form_data);
   const rawFormData = { ...bootstrapData.form_data };
+
   const bootstrappedState = {
     ...bootstrapData,
     common: {
@@ -20,11 +21,15 @@ export default function (bootstrapData) {
     isDatasourceMetaLoading: false,
     isStarred: false,
   };
+
   const slice = bootstrappedState.slice;
+
   const sliceFormData = slice
     ? getFormDataFromControls(getControlsState(bootstrapData, slice.form_data))
     : null;
+
   const chartKey = getChartKey(bootstrappedState);
+
   return {
     featureFlags: bootstrapData.common.feature_flags,
     charts: {
@@ -36,7 +41,7 @@ export default function (bootstrapData) {
         chartUpdateStartTime: now(),
         latestQueryFormData: getFormDataFromControls(controls),
         sliceFormData,
-        queryRequest: null,
+        queryController: null,
         queryResponse: null,
         triggerQuery: true,
         lastRendered: 0,


Mime
View raw message