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 <[email protected]> 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,
