mirror of https://github.com/apache/superset.git
Implement SuperChart and enable the chart plugins (#6154)
* setup plugin and add SuperChart * Integrate SuperChart into Chart.jsx * Add vizType as class name * Remove .slice_container * add snakeCase to sanitize class name * Remove old code to load charts * Fix supportedAnnotationTypes * Update AnnotationTypes, remove unnecessary imports and dependency from VIZ_TYPES * remove index.js and update unit test * resolve tree map issue * fix issue with annotation types * fix proptypes * add ) * address a few comments * create bound functions * bound more functions * add reselect * use reselect for chartprops * improve performance with reselect * remove unused props * Remove getFilters() and update table test * Remove unused code * Delete adaptors * switch to react-loadable * Rewrite with reloadable * Add timeout back * remove loading * update table unit test * remove pastDelay
This commit is contained in:
parent
9580103c22
commit
5c02e3199b
|
@ -104,6 +104,7 @@
|
|||
"react-dom": "^16.4.1",
|
||||
"react-gravatar": "^2.6.1",
|
||||
"react-hot-loader": "^4.3.6",
|
||||
"react-loadable": "^5.5.0",
|
||||
"react-map-gl": "^3.0.4",
|
||||
"react-markdown": "^3.3.0",
|
||||
"react-redux": "^5.0.2",
|
||||
|
@ -122,6 +123,7 @@
|
|||
"redux-localstorage": "^0.4.1",
|
||||
"redux-thunk": "^2.1.0",
|
||||
"redux-undo": "^1.0.0-beta9-9-7",
|
||||
"reselect": "^4.0.0",
|
||||
"shortid": "^2.2.6",
|
||||
"sprintf-js": "^1.1.1",
|
||||
"srcdoc-polyfill": "^1.0.0",
|
||||
|
|
|
@ -1,81 +0,0 @@
|
|||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import sinon from 'sinon';
|
||||
|
||||
import { chart as initChart } from '../../../src/chart/chartReducer';
|
||||
import Chart from '../../../src/chart/Chart';
|
||||
import ChartBody from '../../../src/chart/ChartBody';
|
||||
import Loading from '../../../src/components/Loading';
|
||||
|
||||
describe('Chart', () => {
|
||||
const chart = {
|
||||
...initChart,
|
||||
queryResponse: {
|
||||
form_data: {},
|
||||
error: null,
|
||||
status: 'success',
|
||||
},
|
||||
};
|
||||
const mockedProps = {
|
||||
...chart,
|
||||
id: 223,
|
||||
containerId: 'slice-container-223',
|
||||
datasource: {},
|
||||
formData: {},
|
||||
vizType: 'pie',
|
||||
height: 300,
|
||||
width: 400,
|
||||
actions: {
|
||||
runQuery: () => {},
|
||||
},
|
||||
};
|
||||
|
||||
let wrapper;
|
||||
beforeEach(() => {
|
||||
wrapper = shallow(
|
||||
<Chart {...mockedProps} />,
|
||||
);
|
||||
});
|
||||
describe('renderVis', () => {
|
||||
let stub;
|
||||
beforeEach(() => {
|
||||
stub = sinon.stub(wrapper.instance(), 'renderVis');
|
||||
});
|
||||
afterEach(() => {
|
||||
stub.restore();
|
||||
});
|
||||
|
||||
it('should not call when loading', () => {
|
||||
const prevProp = wrapper.props();
|
||||
wrapper.setProps({
|
||||
height: 100,
|
||||
});
|
||||
wrapper.instance().componentDidUpdate(prevProp);
|
||||
expect(stub.callCount).toBe(0);
|
||||
});
|
||||
|
||||
it('should call after chart stop loading', () => {
|
||||
const prevProp = wrapper.props();
|
||||
wrapper.setProps({
|
||||
chartStatus: 'success',
|
||||
});
|
||||
wrapper.instance().componentDidUpdate(prevProp);
|
||||
expect(stub.callCount).toBe(1);
|
||||
});
|
||||
|
||||
it('should call after resize', () => {
|
||||
wrapper.setProps({
|
||||
chartStatus: 'rendered',
|
||||
height: 100,
|
||||
});
|
||||
expect(stub.callCount).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('render', () => {
|
||||
it('should render ChartBody after loading is completed', () => {
|
||||
expect(wrapper.find(Loading)).toHaveLength(1);
|
||||
expect(wrapper.find(ChartBody)).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -78,10 +78,4 @@ describe('Chart', () => {
|
|||
wrapper.instance().addFilter();
|
||||
expect(addFilter.callCount).toBe(1);
|
||||
});
|
||||
|
||||
it('should return props.filters when its getFilters method is called', () => {
|
||||
const filters = { column: ['value'] };
|
||||
const wrapper = setup({ filters });
|
||||
expect(wrapper.instance().getFilters()).toBe(filters);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,100 +1,100 @@
|
|||
import $ from 'jquery';
|
||||
import '../../helpers/shim';
|
||||
import tableVis from '../../../src/visualizations/Table/adaptor';
|
||||
import Table from '../../../src/visualizations/Table/Table';
|
||||
import transformProps from '../../../src/visualizations/Table/transformProps';
|
||||
|
||||
describe('table viz', () => {
|
||||
const div = '<div id="slice-container"><div class="dataTables_wrapper"></div></div>';
|
||||
const baseSlice = {
|
||||
selector: '#slice-container',
|
||||
const div = '<div id="slice-container"></div>';
|
||||
const BASE_CHART_PROPS = {
|
||||
height: 100,
|
||||
datasource: {
|
||||
verboseMap: {},
|
||||
},
|
||||
filters: {},
|
||||
formData: {
|
||||
metrics: ['count'],
|
||||
timeseries_limit_metric: null,
|
||||
timeseriesLimitMetric: null,
|
||||
},
|
||||
datasource: {
|
||||
verbose_map: {},
|
||||
},
|
||||
getFilters: () => ({}),
|
||||
removeFilter() {},
|
||||
addFilter() {},
|
||||
width: () => 0,
|
||||
height: () => 0,
|
||||
};
|
||||
const basePayload = {
|
||||
data: {
|
||||
records: [
|
||||
{ gender: 'boy', count: 39245 },
|
||||
{ gender: 'girl', count: 36446 },
|
||||
],
|
||||
columns: ['gender', 'count'],
|
||||
onAddFilter() {},
|
||||
payload: {
|
||||
data: {
|
||||
records: [
|
||||
{ gender: 'boy', count: 39245 },
|
||||
{ gender: 'girl', count: 36446 },
|
||||
],
|
||||
columns: ['gender', 'count'],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
it('renders into a container', () => {
|
||||
const PAYLOAD2 = {
|
||||
data: {
|
||||
records: [
|
||||
{ gender: 'boy', count: 39245, 'SUM(sum_boys)': 48133355 },
|
||||
{ gender: 'girl', count: 36446, 'SUM(sum_boys)': 0 },
|
||||
],
|
||||
columns: ['gender', 'count', 'SUM(sum_boys)'],
|
||||
},
|
||||
};
|
||||
|
||||
let container;
|
||||
let $container;
|
||||
|
||||
beforeEach(() => {
|
||||
$('body').html(div);
|
||||
const container = $(baseSlice.selector);
|
||||
expect(container).toHaveLength(1);
|
||||
container = document.getElementById('slice-container');
|
||||
$container = $(container);
|
||||
});
|
||||
|
||||
it('renders into a container', () => {
|
||||
expect($container.children()).toHaveLength(0);
|
||||
Table(container, transformProps(BASE_CHART_PROPS));
|
||||
expect($container.children()).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('renders header and body datatables in container', () => {
|
||||
$('body').html(div);
|
||||
const container = $(baseSlice.selector);
|
||||
expect($container.find('.dataTable')).toHaveLength(0);
|
||||
Table(container, transformProps(BASE_CHART_PROPS));
|
||||
expect($container.find('.dataTable')).toHaveLength(2);
|
||||
|
||||
expect(container.find('.dataTable')).toHaveLength(0);
|
||||
tableVis(baseSlice, basePayload);
|
||||
expect(container.find('.dataTable')).toHaveLength(2);
|
||||
|
||||
const tableHeader = container.find('.dataTable')[0];
|
||||
const tableHeader = $container.find('.dataTable')[0];
|
||||
expect($(tableHeader).find('thead tr')).toHaveLength(1);
|
||||
expect($(tableHeader).find('th')).toHaveLength(2);
|
||||
|
||||
const tableBody = container.find('.dataTable')[1];
|
||||
const tableBody = $container.find('.dataTable')[1];
|
||||
expect($(tableBody).find('tbody tr')).toHaveLength(2);
|
||||
expect($(tableBody).find('th')).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('hides the sort by column', () => {
|
||||
$('body').html(div);
|
||||
const slice = { ...baseSlice };
|
||||
slice.formData = { ...baseSlice.formData,
|
||||
timeseries_limit_metric: {
|
||||
label: 'SUM(sum_boys)',
|
||||
const chartProps = {
|
||||
...BASE_CHART_PROPS,
|
||||
formData: {
|
||||
...BASE_CHART_PROPS.formData,
|
||||
timeseriesLimitMetric: {
|
||||
label: 'SUM(sum_boys)',
|
||||
},
|
||||
},
|
||||
payload: PAYLOAD2,
|
||||
};
|
||||
const payload = {
|
||||
data: {
|
||||
records: [
|
||||
{ gender: 'boy', count: 39245, 'SUM(sum_boys)': 48133355 },
|
||||
{ gender: 'girl', count: 36446, 'SUM(sum_boys)': 0 },
|
||||
],
|
||||
columns: ['gender', 'count', 'SUM(sum_boys)'],
|
||||
},
|
||||
};
|
||||
tableVis(slice, payload);
|
||||
|
||||
const container = $(slice.selector);
|
||||
const tableHeader = container.find('.dataTable')[0];
|
||||
Table(container, transformProps(chartProps));
|
||||
const tableHeader = $container.find('.dataTable')[0];
|
||||
expect($(tableHeader).find('th')).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('works with empty list for sort by', () => {
|
||||
$('body').html(div);
|
||||
const slice = { ...baseSlice };
|
||||
slice.formData = { ...baseSlice.formData,
|
||||
timeseries_limit_metric: [],
|
||||
};
|
||||
const payload = {
|
||||
data: {
|
||||
records: [
|
||||
{ gender: 'boy', count: 39245, 'SUM(sum_boys)': 48133355 },
|
||||
{ gender: 'girl', count: 36446, 'SUM(sum_boys)': 0 },
|
||||
],
|
||||
columns: ['gender', 'count', 'SUM(sum_boys)'],
|
||||
const chartProps = {
|
||||
...BASE_CHART_PROPS,
|
||||
formData: {
|
||||
...BASE_CHART_PROPS.formData,
|
||||
timeseriesLimitMetric: [],
|
||||
},
|
||||
payload: PAYLOAD2,
|
||||
};
|
||||
tableVis(slice, payload);
|
||||
|
||||
const container = $(slice.selector);
|
||||
const tableBody = container.find('.dataTable')[1];
|
||||
Table(container, transformProps(chartProps));
|
||||
const tableBody = $container.find('.dataTable')[1];
|
||||
expect($(tableBody).find('th')).toHaveLength(3);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,25 +1,23 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Tooltip } from 'react-bootstrap';
|
||||
import dompurify from 'dompurify';
|
||||
|
||||
import ChartBody from './ChartBody';
|
||||
import Loading from '../components/Loading';
|
||||
import { snakeCase } from 'lodash';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import { Tooltip } from 'react-bootstrap';
|
||||
import { Logger, LOG_ACTIONS_RENDER_CHART } from '../logger';
|
||||
import StackTraceMessage from '../components/StackTraceMessage';
|
||||
import Loading from '../components/Loading';
|
||||
import RefreshChartOverlay from '../components/RefreshChartOverlay';
|
||||
import visPromiseLookup from '../visualizations';
|
||||
import sandboxedEval from '../modules/sandbox';
|
||||
import StackTraceMessage from '../components/StackTraceMessage';
|
||||
import ChartProps from '../visualizations/core/models/ChartProps';
|
||||
import SuperChart from '../visualizations/core/components/SuperChart';
|
||||
import './chart.css';
|
||||
|
||||
const propTypes = {
|
||||
annotationData: PropTypes.object,
|
||||
actions: PropTypes.object,
|
||||
chartId: PropTypes.number.isRequired,
|
||||
containerId: PropTypes.string.isRequired,
|
||||
datasource: PropTypes.object.isRequired,
|
||||
filters: PropTypes.object,
|
||||
formData: PropTypes.object.isRequired,
|
||||
headerHeight: PropTypes.number,
|
||||
height: PropTypes.number,
|
||||
width: PropTypes.number,
|
||||
setControlValue: PropTypes.func,
|
||||
|
@ -28,46 +26,34 @@ const propTypes = {
|
|||
// state
|
||||
chartAlert: PropTypes.string,
|
||||
chartStatus: PropTypes.string,
|
||||
chartUpdateEndTime: PropTypes.number,
|
||||
chartUpdateStartTime: PropTypes.number,
|
||||
latestQueryFormData: PropTypes.object,
|
||||
queryResponse: PropTypes.object,
|
||||
lastRendered: PropTypes.number,
|
||||
triggerQuery: PropTypes.bool,
|
||||
refreshOverlayVisible: PropTypes.bool,
|
||||
errorMessage: PropTypes.node,
|
||||
// dashboard callbacks
|
||||
addFilter: PropTypes.func,
|
||||
getFilters: PropTypes.func,
|
||||
onQuery: PropTypes.func,
|
||||
onDismissRefreshOverlay: PropTypes.func,
|
||||
};
|
||||
|
||||
const BLANK = {};
|
||||
|
||||
const defaultProps = {
|
||||
addFilter: () => ({}),
|
||||
getFilters: () => ({}),
|
||||
addFilter: () => BLANK,
|
||||
filters: BLANK,
|
||||
setControlValue() {},
|
||||
};
|
||||
|
||||
class Chart extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
// visualizations are lazy-loaded with promises that resolve to a renderVis function
|
||||
this.state = {
|
||||
renderVis: null,
|
||||
};
|
||||
this.state = {};
|
||||
|
||||
// these properties are used by visualizations
|
||||
this.annotationData = props.annotationData;
|
||||
this.containerId = props.containerId;
|
||||
this.selector = `#${this.containerId}`;
|
||||
this.formData = props.formData;
|
||||
this.datasource = props.datasource;
|
||||
this.addFilter = this.addFilter.bind(this);
|
||||
this.getFilters = this.getFilters.bind(this);
|
||||
this.headerHeight = this.headerHeight.bind(this);
|
||||
this.height = this.height.bind(this);
|
||||
this.width = this.width.bind(this);
|
||||
this.visPromise = null;
|
||||
this.createChartProps = ChartProps.createSelector();
|
||||
this.handleAddFilter = this.handleAddFilter.bind(this);
|
||||
this.handleRenderSuccess = this.handleRenderSuccess.bind(this);
|
||||
this.handleRenderFailure = this.handleRenderFailure.bind(this);
|
||||
this.setTooltip = this.setTooltip.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
|
@ -78,110 +64,82 @@ class Chart extends React.PureComponent {
|
|||
this.props.timeout,
|
||||
this.props.chartId,
|
||||
);
|
||||
} else {
|
||||
// when drag/dropping in a dashboard, a chart may be unmounted/remounted but still have data
|
||||
this.renderVis();
|
||||
}
|
||||
|
||||
this.loadAsyncVis(this.props.vizType);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
this.annotationData = nextProps.annotationData;
|
||||
this.containerId = nextProps.containerId;
|
||||
this.selector = `#${this.containerId}`;
|
||||
this.formData = nextProps.formData;
|
||||
this.datasource = nextProps.datasource;
|
||||
if (nextProps.vizType !== this.props.vizType) {
|
||||
this.setState(() => ({ renderVis: null }));
|
||||
this.loadAsyncVis(nextProps.vizType);
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (
|
||||
this.props.queryResponse &&
|
||||
['success', 'rendered'].indexOf(this.props.chartStatus) > -1 &&
|
||||
!this.props.queryResponse.error &&
|
||||
(prevProps.annotationData !== this.props.annotationData ||
|
||||
prevProps.queryResponse !== this.props.queryResponse ||
|
||||
prevProps.height !== this.props.height ||
|
||||
prevProps.width !== this.props.width ||
|
||||
prevProps.lastRendered !== this.props.lastRendered)
|
||||
) {
|
||||
this.renderVis();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.visPromise = null;
|
||||
}
|
||||
|
||||
getFilters() {
|
||||
return this.props.getFilters();
|
||||
}
|
||||
|
||||
setTooltip(tooltip) {
|
||||
this.setState({ tooltip });
|
||||
}
|
||||
|
||||
loadAsyncVis(visType) {
|
||||
this.visPromise = visPromiseLookup[visType];
|
||||
|
||||
this.visPromise()
|
||||
.then((renderVis) => {
|
||||
// ensure Component is still mounted
|
||||
if (this.visPromise) {
|
||||
this.setState({ renderVis }, this.renderVis);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.warn(error); // eslint-disable-line
|
||||
this.props.actions.chartRenderingFailed(error, this.props.chartId);
|
||||
});
|
||||
}
|
||||
|
||||
addFilter(col, vals, merge = true, refresh = true) {
|
||||
handleAddFilter(col, vals, merge = true, refresh = true) {
|
||||
this.props.addFilter(col, vals, merge, refresh);
|
||||
}
|
||||
|
||||
clearError() {
|
||||
this.setState({ errorMsg: null });
|
||||
handleRenderSuccess() {
|
||||
const { actions, chartStatus, chartId, vizType } = this.props;
|
||||
if (chartStatus !== 'rendered') {
|
||||
actions.chartRenderingSucceeded(chartId);
|
||||
}
|
||||
|
||||
Logger.append(LOG_ACTIONS_RENDER_CHART, {
|
||||
slice_id: chartId,
|
||||
viz_type: vizType,
|
||||
start_offset: this.renderStartTime,
|
||||
duration: Logger.getTimestamp() - this.renderStartTime,
|
||||
});
|
||||
}
|
||||
|
||||
width() {
|
||||
return this.props.width;
|
||||
handleRenderFailure(e) {
|
||||
const { actions, chartId } = this.props;
|
||||
console.warn(e); // eslint-disable-line
|
||||
actions.chartRenderingFailed(e, chartId);
|
||||
}
|
||||
|
||||
headerHeight() {
|
||||
return this.props.headerHeight || 0;
|
||||
}
|
||||
prepareChartProps() {
|
||||
const {
|
||||
width,
|
||||
height,
|
||||
annotationData,
|
||||
datasource,
|
||||
filters,
|
||||
formData,
|
||||
queryResponse,
|
||||
setControlValue,
|
||||
} = this.props;
|
||||
|
||||
height() {
|
||||
return this.props.height;
|
||||
}
|
||||
|
||||
error(e) {
|
||||
this.props.actions.chartRenderingFailed(e, this.props.chartId);
|
||||
return this.createChartProps({
|
||||
width,
|
||||
height,
|
||||
annotationData,
|
||||
datasource,
|
||||
filters,
|
||||
formData,
|
||||
onAddFilter: this.handleAddFilter,
|
||||
onError: this.handleRenderFailure,
|
||||
payload: queryResponse,
|
||||
setControlValue,
|
||||
setTooltip: this.setTooltip,
|
||||
});
|
||||
}
|
||||
|
||||
renderTooltip() {
|
||||
if (this.state.tooltip) {
|
||||
const { tooltip } = this.state;
|
||||
|
||||
if (tooltip) {
|
||||
return (
|
||||
<Tooltip
|
||||
className="chart-tooltip"
|
||||
id="chart-tooltip"
|
||||
placement="right"
|
||||
positionTop={this.state.tooltip.y + 30}
|
||||
positionLeft={this.state.tooltip.x + 30}
|
||||
positionTop={tooltip.y + 30}
|
||||
positionLeft={tooltip.x + 30}
|
||||
arrowOffsetTop={10}
|
||||
>
|
||||
{typeof (this.state.tooltip.content) === 'string' ?
|
||||
{typeof (tooltip.content) === 'string' ?
|
||||
<div // eslint-disable-next-line react/no-danger
|
||||
dangerouslySetInnerHTML={{ __html: dompurify.sanitize(this.state.tooltip.content) }}
|
||||
dangerouslySetInnerHTML={{ __html: dompurify.sanitize(tooltip.content) }}
|
||||
/>
|
||||
:
|
||||
this.state.tooltip.content
|
||||
: tooltip.content
|
||||
}
|
||||
</Tooltip>
|
||||
);
|
||||
|
@ -189,85 +147,61 @@ class Chart extends React.PureComponent {
|
|||
return null;
|
||||
}
|
||||
|
||||
renderVis() {
|
||||
const { chartStatus } = this.props;
|
||||
const hasVisPromise = !!this.state.renderVis;
|
||||
// check that we have the render function and data
|
||||
if (hasVisPromise && ['success', 'rendered'].indexOf(chartStatus) > -1) {
|
||||
const { vizType, formData, queryResponse, setControlValue, chartId } = this.props;
|
||||
const renderStart = Logger.getTimestamp();
|
||||
|
||||
try {
|
||||
// Executing user-defined data mutator function
|
||||
if (formData.js_data) {
|
||||
queryResponse.data = sandboxedEval(formData.js_data)(queryResponse.data);
|
||||
}
|
||||
// [re]rendering the visualization
|
||||
this.state.renderVis(this, queryResponse, setControlValue);
|
||||
|
||||
if (chartStatus !== 'rendered') {
|
||||
this.props.actions.chartRenderingSucceeded(chartId);
|
||||
}
|
||||
|
||||
Logger.append(LOG_ACTIONS_RENDER_CHART, {
|
||||
slice_id: chartId,
|
||||
viz_type: vizType,
|
||||
start_offset: renderStart,
|
||||
duration: Logger.getTimestamp() - renderStart,
|
||||
});
|
||||
} catch (e) {
|
||||
console.warn(e); // eslint-disable-line
|
||||
this.props.actions.chartRenderingFailed(e, chartId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const isLoading = this.props.chartStatus === 'loading' || !this.state.renderVis;
|
||||
const {
|
||||
width,
|
||||
height,
|
||||
chartAlert,
|
||||
chartStatus,
|
||||
errorMessage,
|
||||
onDismissRefreshOverlay,
|
||||
onQuery,
|
||||
queryResponse,
|
||||
refreshOverlayVisible,
|
||||
vizType,
|
||||
} = this.props;
|
||||
|
||||
const isLoading = chartStatus === 'loading';
|
||||
|
||||
// this allows <Loading /> to be positioned in the middle of the chart
|
||||
const containerStyles = isLoading ? { height: this.height(), width: this.width() } : null;
|
||||
const containerStyles = isLoading ? { height, width } : null;
|
||||
const isFaded = refreshOverlayVisible && !errorMessage;
|
||||
const skipChartRendering = isLoading || !!chartAlert;
|
||||
this.renderStartTime = Logger.getTimestamp();
|
||||
|
||||
return (
|
||||
<div className={`chart-container ${isLoading ? 'is-loading' : ''}`} style={containerStyles}>
|
||||
<div
|
||||
className={`chart-container ${isLoading ? 'is-loading' : ''}`}
|
||||
style={containerStyles}
|
||||
>
|
||||
{this.renderTooltip()}
|
||||
|
||||
{isLoading && <Loading size={50} />}
|
||||
|
||||
{this.props.chartAlert && (
|
||||
{chartAlert && (
|
||||
<StackTraceMessage
|
||||
message={this.props.chartAlert}
|
||||
queryResponse={this.props.queryResponse}
|
||||
message={chartAlert}
|
||||
queryResponse={queryResponse}
|
||||
/>
|
||||
)}
|
||||
|
||||
{!isLoading &&
|
||||
!this.props.chartAlert &&
|
||||
this.props.refreshOverlayVisible &&
|
||||
!this.props.errorMessage &&
|
||||
this.container && (
|
||||
<RefreshChartOverlay
|
||||
height={this.height()}
|
||||
width={this.width()}
|
||||
onQuery={this.props.onQuery}
|
||||
onDismiss={this.props.onDismissRefreshOverlay}
|
||||
/>
|
||||
)}
|
||||
{!isLoading && !chartAlert && isFaded && (
|
||||
<RefreshChartOverlay
|
||||
width={width}
|
||||
height={height}
|
||||
onQuery={onQuery}
|
||||
onDismiss={onDismissRefreshOverlay}
|
||||
/>
|
||||
)}
|
||||
|
||||
{!isLoading &&
|
||||
!this.props.chartAlert && (
|
||||
<ChartBody
|
||||
containerId={this.containerId}
|
||||
vizType={this.props.vizType}
|
||||
height={this.height}
|
||||
width={this.width}
|
||||
faded={
|
||||
this.props.refreshOverlayVisible && !this.props.errorMessage
|
||||
}
|
||||
ref={(inner) => {
|
||||
this.container = inner;
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<SuperChart
|
||||
className={`slice_container ${snakeCase(vizType)} ${isFaded ? ' faded' : ''}`}
|
||||
chartType={vizType}
|
||||
chartProps={skipChartRendering ? null : this.prepareChartProps()}
|
||||
onRenderSuccess={this.handleRenderSuccess}
|
||||
onRenderFailure={this.handleRenderFailure}
|
||||
skipRendering={skipChartRendering}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const propTypes = {
|
||||
containerId: PropTypes.string.isRequired,
|
||||
vizType: PropTypes.string.isRequired,
|
||||
height: PropTypes.func.isRequired,
|
||||
width: PropTypes.func.isRequired,
|
||||
faded: PropTypes.bool,
|
||||
};
|
||||
|
||||
class ChartBody extends React.PureComponent {
|
||||
height() {
|
||||
return this.props.height();
|
||||
}
|
||||
|
||||
width() {
|
||||
return this.props.width();
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div
|
||||
id={this.props.containerId}
|
||||
className={`slice_container ${this.props.vizType}${this.props.faded ? ' faded' : ''}`}
|
||||
ref={(el) => { this.el = el; }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ChartBody.propTypes = propTypes;
|
||||
|
||||
export default ChartBody;
|
|
@ -5,6 +5,10 @@ import { SupersetClient } from '@superset-ui/core';
|
|||
import { toggleCheckbox } from './modules/utils';
|
||||
import setupClient from './setup/setupClient';
|
||||
import setupColors from './setup/setupColors';
|
||||
import setupPlugins from './setup/setupPlugins';
|
||||
|
||||
setupColors();
|
||||
setupPlugins();
|
||||
|
||||
$(document).ready(function () {
|
||||
$(':checkbox[data-checkbox-api-prefix]').change(function () {
|
||||
|
@ -29,7 +33,6 @@ $(document).ready(function () {
|
|||
|
||||
export function appSetup() {
|
||||
setupClient();
|
||||
setupColors();
|
||||
|
||||
// A set of hacks to allow apps to run within a FAB template
|
||||
// this allows for the server side generated menus to function
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
import cx from 'classnames';
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { exportChart } from '../../../explore/exploreUtils';
|
||||
import SliceHeader from '../SliceHeader';
|
||||
import ChartContainer from '../../../chart/ChartContainer';
|
||||
import MissingChart from '../MissingChart';
|
||||
import { chartPropType } from '../../../chart/chartReducer';
|
||||
import { slicePropShape } from '../../util/propShapes';
|
||||
import { VIZ_TYPES } from '../../../visualizations';
|
||||
|
||||
const propTypes = {
|
||||
id: PropTypes.number.isRequired,
|
||||
|
@ -39,7 +37,7 @@ const RESIZE_TIMEOUT = 350;
|
|||
const SHOULD_UPDATE_ON_PROP_CHANGES = Object.keys(propTypes).filter(
|
||||
prop => prop !== 'width' && prop !== 'height',
|
||||
);
|
||||
const OVERFLOWABLE_VIZ_TYPES = new Set([VIZ_TYPES.filter_box]);
|
||||
const OVERFLOWABLE_VIZ_TYPES = new Set(['filter_box']);
|
||||
|
||||
class Chart extends React.Component {
|
||||
constructor(props) {
|
||||
|
@ -53,7 +51,6 @@ class Chart extends React.Component {
|
|||
this.exploreChart = this.exploreChart.bind(this);
|
||||
this.exportCSV = this.exportCSV.bind(this);
|
||||
this.forceRefresh = this.forceRefresh.bind(this);
|
||||
this.getFilters = this.getFilters.bind(this);
|
||||
this.resize = this.resize.bind(this);
|
||||
this.setDescriptionRef = this.setDescriptionRef.bind(this);
|
||||
this.setHeaderRef = this.setHeaderRef.bind(this);
|
||||
|
@ -92,10 +89,6 @@ class Chart extends React.Component {
|
|||
clearTimeout(this.resizeTimeout);
|
||||
}
|
||||
|
||||
getFilters() {
|
||||
return this.props.filters;
|
||||
}
|
||||
|
||||
getChartHeight() {
|
||||
const headerHeight = this.getHeaderHeight();
|
||||
const descriptionHeight =
|
||||
|
@ -147,6 +140,7 @@ class Chart extends React.Component {
|
|||
datasource,
|
||||
isExpanded,
|
||||
editMode,
|
||||
filters,
|
||||
formData,
|
||||
updateSliceName,
|
||||
sliceName,
|
||||
|
@ -214,27 +208,20 @@ class Chart extends React.Component {
|
|||
)}
|
||||
>
|
||||
<ChartContainer
|
||||
containerId={`slice-container-${id}`}
|
||||
chartId={id}
|
||||
datasource={datasource}
|
||||
formData={formData}
|
||||
headerHeight={this.getHeaderHeight()}
|
||||
height={this.getChartHeight()}
|
||||
width={width}
|
||||
timeout={timeout}
|
||||
vizType={slice.viz_type}
|
||||
height={this.getChartHeight()}
|
||||
addFilter={this.addFilter}
|
||||
getFilters={this.getFilters}
|
||||
annotationData={chart.annotationData}
|
||||
chartAlert={chart.chartAlert}
|
||||
chartId={id}
|
||||
chartStatus={chart.chartStatus}
|
||||
chartUpdateEndTime={chart.chartUpdateEndTime}
|
||||
chartUpdateStartTime={chart.chartUpdateStartTime}
|
||||
latestQueryFormData={chart.latestQueryFormData}
|
||||
lastRendered={chart.lastRendered}
|
||||
datasource={datasource}
|
||||
filters={filters}
|
||||
formData={formData}
|
||||
queryResponse={chart.queryResponse}
|
||||
queryController={chart.queryController}
|
||||
timeout={timeout}
|
||||
triggerQuery={chart.triggerQuery}
|
||||
vizType={slice.viz_type}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -39,30 +39,23 @@ class ExploreChartPanel extends React.PureComponent {
|
|||
<ParentSize>
|
||||
{({ width, height }) => (width > 0 && height > 0) && (
|
||||
<ChartContainer
|
||||
chartId={chart.id}
|
||||
containerId={this.props.containerId}
|
||||
datasource={this.props.datasource}
|
||||
formData={this.props.form_data}
|
||||
width={Math.floor(width)}
|
||||
height={parseInt(this.props.height, 10) - headerHeight}
|
||||
slice={this.props.slice}
|
||||
setControlValue={this.props.actions.setControlValue}
|
||||
timeout={this.props.timeout}
|
||||
vizType={this.props.vizType}
|
||||
refreshOverlayVisible={this.props.refreshOverlayVisible}
|
||||
errorMessage={this.props.errorMessage}
|
||||
onQuery={this.props.onQuery}
|
||||
onDismissRefreshOverlay={this.props.onDismissRefreshOverlay}
|
||||
annotationData={chart.annotationData}
|
||||
chartAlert={chart.chartAlert}
|
||||
chartId={chart.id}
|
||||
chartStatus={chart.chartStatus}
|
||||
chartUpdateEndTime={chart.chartUpdateEndTime}
|
||||
chartUpdateStartTime={chart.chartUpdateStartTime}
|
||||
latestQueryFormData={chart.latestQueryFormData}
|
||||
lastRendered={chart.lastRendered}
|
||||
datasource={this.props.datasource}
|
||||
errorMessage={this.props.errorMessage}
|
||||
formData={this.props.form_data}
|
||||
onDismissRefreshOverlay={this.props.onDismissRefreshOverlay}
|
||||
onQuery={this.props.onQuery}
|
||||
queryResponse={chart.queryResponse}
|
||||
queryController={chart.queryController}
|
||||
refreshOverlayVisible={this.props.refreshOverlayVisible}
|
||||
setControlValue={this.props.actions.setControlValue}
|
||||
timeout={this.props.timeout}
|
||||
triggerQuery={chart.triggerQuery}
|
||||
vizType={this.props.vizType}
|
||||
/>
|
||||
)}
|
||||
</ParentSize>
|
||||
|
|
|
@ -9,20 +9,18 @@ import SelectControl from './SelectControl';
|
|||
import TextControl from './TextControl';
|
||||
import CheckboxControl from './CheckboxControl';
|
||||
|
||||
import AnnotationTypes, {
|
||||
DEFAULT_ANNOTATION_TYPE,
|
||||
import ANNOTATION_TYPES, {
|
||||
ANNOTATION_SOURCE_TYPES,
|
||||
getAnnotationSourceTypeLabels,
|
||||
getAnnotationTypeLabel,
|
||||
getSupportedSourceTypes,
|
||||
getSupportedAnnotationTypes,
|
||||
ANNOTATION_TYPES_METADATA,
|
||||
DEFAULT_ANNOTATION_TYPE,
|
||||
requiresQuery,
|
||||
ANNOTATION_SOURCE_TYPES_METADATA,
|
||||
} from '../../../modules/AnnotationTypes';
|
||||
|
||||
import PopoverSection from '../../../components/PopoverSection';
|
||||
import ControlHeader from '../ControlHeader';
|
||||
import { nonEmpty } from '../../validators';
|
||||
import vizTypes from '../../visTypes';
|
||||
import getChartMetadataRegistry from '../../../visualizations/core/registries/ChartMetadataRegistrySingleton';
|
||||
|
||||
import { t } from '../../../locales';
|
||||
import getCategoricalSchemeRegistry from '../../../modules/colors/CategoricalSchemeRegistrySingleton';
|
||||
|
@ -148,8 +146,23 @@ export default class AnnotationLayer extends React.PureComponent {
|
|||
}
|
||||
}
|
||||
|
||||
getSupportedSourceTypes(annotationType) {
|
||||
// Get vis types that can be source.
|
||||
const sources = getChartMetadataRegistry().entries()
|
||||
.filter(({ value: chartMetadata }) => chartMetadata.canBeAnnotationType(annotationType))
|
||||
.map(({ key, value: chartMetadata }) => ({
|
||||
value: key,
|
||||
label: chartMetadata.name,
|
||||
}));
|
||||
// Prepend native source if applicable
|
||||
if (ANNOTATION_TYPES_METADATA[annotationType].supportNativeSource) {
|
||||
sources.unshift(ANNOTATION_SOURCE_TYPES_METADATA.NATIVE);
|
||||
}
|
||||
return sources;
|
||||
}
|
||||
|
||||
isValidFormula(value, annotationType) {
|
||||
if (annotationType === AnnotationTypes.FORMULA) {
|
||||
if (annotationType === ANNOTATION_TYPES.FORMULA) {
|
||||
try {
|
||||
mathjs
|
||||
.parse(value)
|
||||
|
@ -166,10 +179,10 @@ export default class AnnotationLayer extends React.PureComponent {
|
|||
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) {
|
||||
if (annotationType === ANNOTATION_TYPES.EVENT) {
|
||||
errors.push(nonEmpty(timeColumn));
|
||||
}
|
||||
if (annotationType === AnnotationTypes.INTERVAL) {
|
||||
if (annotationType === ANNOTATION_TYPES.INTERVAL) {
|
||||
errors.push(nonEmpty(timeColumn));
|
||||
errors.push(nonEmpty(intervalEndColumn));
|
||||
}
|
||||
|
@ -223,14 +236,18 @@ export default class AnnotationLayer extends React.PureComponent {
|
|||
});
|
||||
});
|
||||
} else if (requiresQuery(sourceType)) {
|
||||
SupersetClient.get({ endpoint: '/superset/user_slices' }).then(({ json }) =>
|
||||
SupersetClient.get({ endpoint: '/superset/user_slices' }).then(({ json }) => {
|
||||
const registry = getChartMetadataRegistry();
|
||||
this.setState({
|
||||
isLoadingOptions: false,
|
||||
valueOptions: json
|
||||
.filter(x => getSupportedSourceTypes(annotationType).find(v => v === x.viz_type))
|
||||
.filter((x) => {
|
||||
const metadata = registry.get(x.viz_type);
|
||||
return metadata && metadata.canBeAnnotationType(annotationType);
|
||||
})
|
||||
.map(x => ({ value: x.id, label: x.title, slice: x })),
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
isLoadingOptions: false,
|
||||
|
@ -282,11 +299,11 @@ export default class AnnotationLayer extends React.PureComponent {
|
|||
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 : ''))
|
||||
[${this.getSupportedSourceTypes(annotationType)
|
||||
.map(x => x.label)
|
||||
.join(', ')}]`;
|
||||
}
|
||||
} else if (annotationType === AnnotationTypes.FORMULA) {
|
||||
} else if (annotationType === ANNOTATION_TYPES.FORMULA) {
|
||||
label = 'Formula';
|
||||
description = `Expects a formula with depending time parameter 'x'
|
||||
in milliseconds since epoch. mathjs is used to evaluate the formulas.
|
||||
|
@ -309,7 +326,7 @@ export default class AnnotationLayer extends React.PureComponent {
|
|||
/>
|
||||
);
|
||||
}
|
||||
if (annotationType === AnnotationTypes.FORMULA) {
|
||||
if (annotationType === ANNOTATION_TYPES.FORMULA) {
|
||||
return (
|
||||
<TextControl
|
||||
name="annotation-layer-value"
|
||||
|
@ -356,13 +373,13 @@ export default class AnnotationLayer extends React.PureComponent {
|
|||
info={`This section allows you to configure how to use the slice
|
||||
to generate annotations.`}
|
||||
>
|
||||
{(annotationType === AnnotationTypes.EVENT ||
|
||||
annotationType === AnnotationTypes.INTERVAL) && (
|
||||
{(annotationType === ANNOTATION_TYPES.EVENT ||
|
||||
annotationType === ANNOTATION_TYPES.INTERVAL) && (
|
||||
<SelectControl
|
||||
hovered
|
||||
name="annotation-layer-time-column"
|
||||
label={
|
||||
annotationType === AnnotationTypes.INTERVAL
|
||||
annotationType === ANNOTATION_TYPES.INTERVAL
|
||||
? 'Interval Start column'
|
||||
: 'Event Time Column'
|
||||
}
|
||||
|
@ -374,7 +391,7 @@ export default class AnnotationLayer extends React.PureComponent {
|
|||
onChange={v => this.setState({ timeColumn: v })}
|
||||
/>
|
||||
)}
|
||||
{annotationType === AnnotationTypes.INTERVAL && (
|
||||
{annotationType === ANNOTATION_TYPES.INTERVAL && (
|
||||
<SelectControl
|
||||
hovered
|
||||
name="annotation-layer-intervalEnd"
|
||||
|
@ -395,7 +412,7 @@ export default class AnnotationLayer extends React.PureComponent {
|
|||
value={titleColumn}
|
||||
onChange={v => this.setState({ titleColumn: v })}
|
||||
/>
|
||||
{annotationType !== AnnotationTypes.TIME_SERIES && (
|
||||
{annotationType !== ANNOTATION_TYPES.TIME_SERIES && (
|
||||
<SelectControl
|
||||
hovered
|
||||
name="annotation-layer-title"
|
||||
|
@ -553,7 +570,7 @@ export default class AnnotationLayer extends React.PureComponent {
|
|||
value={width}
|
||||
onChange={v => this.setState({ width: v })}
|
||||
/>
|
||||
{annotationType === AnnotationTypes.TIME_SERIES && (
|
||||
{annotationType === ANNOTATION_TYPES.TIME_SERIES && (
|
||||
<CheckboxControl
|
||||
hovered
|
||||
name="annotation-layer-show-markers"
|
||||
|
@ -563,7 +580,7 @@ export default class AnnotationLayer extends React.PureComponent {
|
|||
onChange={v => this.setState({ showMarkers: v })}
|
||||
/>
|
||||
)}
|
||||
{annotationType === AnnotationTypes.TIME_SERIES && (
|
||||
{annotationType === ANNOTATION_TYPES.TIME_SERIES && (
|
||||
<CheckboxControl
|
||||
hovered
|
||||
name="annotation-layer-hide-line"
|
||||
|
@ -580,6 +597,13 @@ export default class AnnotationLayer extends React.PureComponent {
|
|||
render() {
|
||||
const { isNew, name, annotationType, sourceType, show } = this.state;
|
||||
const isValid = this.isValidForm();
|
||||
|
||||
const metadata = getChartMetadataRegistry().get(this.props.vizType);
|
||||
const supportedAnnotationTypes = metadata
|
||||
? metadata.supportedAnnotationTypes.map(type => ANNOTATION_TYPES_METADATA[type])
|
||||
: [];
|
||||
const supportedSourceTypes = this.getSupportedSourceTypes(annotationType);
|
||||
|
||||
return (
|
||||
<div>
|
||||
{this.props.error && <span style={{ color: 'red' }}>ERROR: {this.props.error}</span>}
|
||||
|
@ -610,23 +634,17 @@ 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={supportedAnnotationTypes}
|
||||
value={annotationType}
|
||||
onChange={this.handleAnnotationType}
|
||||
/>
|
||||
{!!getSupportedSourceTypes(annotationType).length && (
|
||||
{!!supportedSourceTypes.length && (
|
||||
<SelectControl
|
||||
hovered
|
||||
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={supportedSourceTypes}
|
||||
value={sourceType}
|
||||
onChange={this.handleAnnotationSourceType}
|
||||
/>
|
||||
|
|
|
@ -1,81 +1,50 @@
|
|||
import { VIZ_TYPES } from '../visualizations';
|
||||
import vizTypes from '../explore/visTypes';
|
||||
|
||||
export const ANNOTATION_TYPES = {
|
||||
FORMULA: 'FORMULA',
|
||||
EVENT: 'EVENT',
|
||||
INTERVAL: 'INTERVAL',
|
||||
TIME_SERIES: 'TIME_SERIES',
|
||||
};
|
||||
|
||||
export const ANNOTATION_TYPE_LABELS = {
|
||||
FORMULA: 'Formula ',
|
||||
EVENT: 'Event',
|
||||
INTERVAL: 'Interval',
|
||||
TIME_SERIES: 'Time Series',
|
||||
};
|
||||
|
||||
export function getAnnotationTypeLabel(annotationType) {
|
||||
return ANNOTATION_TYPE_LABELS[annotationType];
|
||||
function extractTypes(metadata) {
|
||||
return Object.keys(metadata)
|
||||
.reduce((prev, key) => {
|
||||
const result = prev;
|
||||
result[key] = key;
|
||||
return result;
|
||||
}, {});
|
||||
}
|
||||
|
||||
export const ANNOTATION_TYPES_METADATA = {
|
||||
FORMULA: {
|
||||
value: 'FORMULA',
|
||||
label: 'Formula',
|
||||
},
|
||||
EVENT: {
|
||||
value: 'EVENT',
|
||||
label: 'Event',
|
||||
supportNativeSource: true,
|
||||
},
|
||||
INTERVAL: {
|
||||
value: 'INTERVAL',
|
||||
label: 'Interval',
|
||||
supportNativeSource: true,
|
||||
},
|
||||
TIME_SERIES: {
|
||||
value: 'TIME_SERIES',
|
||||
label: 'Time Series',
|
||||
},
|
||||
};
|
||||
|
||||
export const ANNOTATION_TYPES = extractTypes(ANNOTATION_TYPES_METADATA);
|
||||
|
||||
export const DEFAULT_ANNOTATION_TYPE = ANNOTATION_TYPES.FORMULA;
|
||||
|
||||
export const ANNOTATION_SOURCE_TYPES = {
|
||||
NATIVE: 'NATIVE',
|
||||
...VIZ_TYPES,
|
||||
export const ANNOTATION_SOURCE_TYPES_METADATA = {
|
||||
NATIVE: {
|
||||
value: 'NATIVE',
|
||||
label: 'Superset annotation',
|
||||
},
|
||||
};
|
||||
|
||||
export function getAnnotationSourceTypeLabels(sourceType) {
|
||||
return ANNOTATION_SOURCE_TYPES.NATIVE === sourceType ? 'Superset annotation' :
|
||||
vizTypes[sourceType].label;
|
||||
}
|
||||
export const ANNOTATION_SOURCE_TYPES = extractTypes(ANNOTATION_SOURCE_TYPES_METADATA);
|
||||
|
||||
export function requiresQuery(annotationSourceType) {
|
||||
return !!annotationSourceType;
|
||||
}
|
||||
|
||||
// Map annotation type to annotation source type
|
||||
const SUPPORTED_SOURCE_TYPE_MAP = {
|
||||
[ANNOTATION_TYPES.EVENT]: [
|
||||
ANNOTATION_SOURCE_TYPES.NATIVE,
|
||||
ANNOTATION_SOURCE_TYPES.table,
|
||||
],
|
||||
[ANNOTATION_TYPES.INTERVAL]: [
|
||||
ANNOTATION_SOURCE_TYPES.NATIVE,
|
||||
ANNOTATION_SOURCE_TYPES.table,
|
||||
],
|
||||
[ANNOTATION_TYPES.TIME_SERIES]: [
|
||||
ANNOTATION_SOURCE_TYPES.line,
|
||||
],
|
||||
};
|
||||
|
||||
export function getSupportedSourceTypes(annotationType) {
|
||||
return SUPPORTED_SOURCE_TYPE_MAP[annotationType] || [];
|
||||
}
|
||||
|
||||
// Map from viz type to supported annotation
|
||||
const SUPPORTED_ANNOTATIONS = {
|
||||
[VIZ_TYPES.line]: [
|
||||
ANNOTATION_TYPES.TIME_SERIES,
|
||||
ANNOTATION_TYPES.INTERVAL,
|
||||
ANNOTATION_TYPES.EVENT,
|
||||
ANNOTATION_TYPES.FORMULA,
|
||||
],
|
||||
[VIZ_TYPES.bar]: [
|
||||
ANNOTATION_TYPES.INTERVAL,
|
||||
ANNOTATION_TYPES.EVENT,
|
||||
],
|
||||
[VIZ_TYPES.area]: [
|
||||
ANNOTATION_TYPES.INTERVAL,
|
||||
ANNOTATION_TYPES.EVENT,
|
||||
],
|
||||
};
|
||||
|
||||
export function getSupportedAnnotationTypes(vizType) {
|
||||
return SUPPORTED_ANNOTATIONS[vizType] || [];
|
||||
}
|
||||
|
||||
const NATIVE_COLUMN_NAMES = {
|
||||
timeColumn: 'start_dttm',
|
||||
intervalEndColumn: 'end_dttm',
|
||||
|
@ -91,4 +60,3 @@ export function applyNativeColumns(annotation) {
|
|||
}
|
||||
|
||||
export default ANNOTATION_TYPES;
|
||||
|
||||
|
|
|
@ -199,20 +199,6 @@ export function d3format(format, number) {
|
|||
}
|
||||
}
|
||||
|
||||
// Slice objects interact with their context through objects that implement
|
||||
// this controllerInterface (dashboard, explore, standalone)
|
||||
export const controllerInterface = {
|
||||
type: null,
|
||||
done: () => {},
|
||||
error: () => {},
|
||||
always: () => {},
|
||||
addFiler: () => {},
|
||||
setFilter: () => {},
|
||||
getFilters: () => false,
|
||||
removeFilter: () => {},
|
||||
filters: {},
|
||||
};
|
||||
|
||||
export function formatSelectOptionsForRange(start, end) {
|
||||
// outputs array of arrays
|
||||
// formatSelectOptionsForRange(1, 5)
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
import LegacyChartPreset from '../visualizations/presets/LegacyChartPreset';
|
||||
|
||||
export default function setupPlugins() {
|
||||
new LegacyChartPreset().register();
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
import createAdaptor from '../../utils/createAdaptor';
|
||||
import Component from './BigNumber';
|
||||
import transformProps from './transformProps';
|
||||
|
||||
export default createAdaptor(Component, transformProps);
|
|
@ -1,10 +1,10 @@
|
|||
.slice_container.cal_heatmap {
|
||||
.cal_heatmap {
|
||||
padding: 10px;
|
||||
position: static !important;
|
||||
overflow: auto !important;
|
||||
}
|
||||
|
||||
.cal_heatmap .slice_container .ch-tooltip {
|
||||
.cal_heatmap .ch-tooltip {
|
||||
margin-left: 20px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
import createAdaptor from '../../utils/createAdaptor';
|
||||
import Component from './ReactCalendar';
|
||||
import transformProps from './transformProps';
|
||||
|
||||
export default createAdaptor(Component, transformProps);
|
|
@ -1,5 +0,0 @@
|
|||
import createAdaptor from '../../utils/createAdaptor';
|
||||
import Component from './ReactChord';
|
||||
import transformProps from './transformProps';
|
||||
|
||||
export default createAdaptor(Component, transformProps);
|
|
@ -1,5 +0,0 @@
|
|||
import createAdaptor from '../../utils/createAdaptor';
|
||||
import Component from './ReactCountryMap';
|
||||
import transformProps from './transformProps';
|
||||
|
||||
export default createAdaptor(Component, transformProps);
|
|
@ -1,5 +0,0 @@
|
|||
import createAdaptor from '../../utils/createAdaptor';
|
||||
import Component from './EventFlow';
|
||||
import transformProps from './transformProps';
|
||||
|
||||
export default createAdaptor(Component, transformProps);
|
|
@ -26,7 +26,7 @@ ul.select2-results div.filter_box{
|
|||
border-width: 1px;
|
||||
border-color: transparent;
|
||||
}
|
||||
.filter_box.slice_container {
|
||||
.filter_box {
|
||||
padding: 10px;
|
||||
overflow: visible !important;
|
||||
}
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
import createAdaptor from '../../utils/createAdaptor';
|
||||
import Component from './FilterBox';
|
||||
import transformProps from './transformProps';
|
||||
|
||||
export default createAdaptor(Component, transformProps);
|
|
@ -1,5 +0,0 @@
|
|||
import createAdaptor from '../../utils/createAdaptor';
|
||||
import Component from './ReactForceDirected';
|
||||
import transformProps from './transformProps';
|
||||
|
||||
export default createAdaptor(Component, transformProps);
|
|
@ -1,5 +0,0 @@
|
|||
import createAdaptor from '../../utils/createAdaptor';
|
||||
import Component from './ReactHeatmap';
|
||||
import transformProps from './transformProps';
|
||||
|
||||
export default createAdaptor(Component, transformProps);
|
|
@ -1,5 +0,0 @@
|
|||
import createAdaptor from '../../utils/createAdaptor';
|
||||
import Component from './Histogram';
|
||||
import transformProps from './transformProps';
|
||||
|
||||
export default createAdaptor(Component, transformProps);
|
|
@ -1,5 +0,0 @@
|
|||
import createAdaptor from '../../utils/createAdaptor';
|
||||
import Component from './HorizonChart';
|
||||
import transformProps from './transformProps';
|
||||
|
||||
export default createAdaptor(Component, transformProps);
|
|
@ -1,5 +0,0 @@
|
|||
import createAdaptor from '../../utils/createAdaptor';
|
||||
import Component from './MapBox';
|
||||
import transformProps from './transformProps';
|
||||
|
||||
export default createAdaptor(Component, transformProps);
|
|
@ -1,5 +0,0 @@
|
|||
import createAdaptor from '../../utils/createAdaptor';
|
||||
import Component from './PairedTTest';
|
||||
import transformProps from './transformProps';
|
||||
|
||||
export default createAdaptor(Component, transformProps);
|
|
@ -1,5 +0,0 @@
|
|||
import createAdaptor from '../../utils/createAdaptor';
|
||||
import Component from './ReactParallelCoordinates';
|
||||
import transformProps from './transformProps';
|
||||
|
||||
export default createAdaptor(Component, transformProps);
|
|
@ -1,5 +0,0 @@
|
|||
import createAdaptor from '../../utils/createAdaptor';
|
||||
import Component from './ReactPartition';
|
||||
import transformProps from './transformProps';
|
||||
|
||||
export default createAdaptor(Component, transformProps);
|
|
@ -1,5 +0,0 @@
|
|||
import createAdaptor from '../../utils/createAdaptor';
|
||||
import Component from './ReactPivotTable';
|
||||
import transformProps from './transformProps';
|
||||
|
||||
export default createAdaptor(Component, transformProps);
|
|
@ -1,5 +0,0 @@
|
|||
import createAdaptor from '../../utils/createAdaptor';
|
||||
import Component from './ReactRose';
|
||||
import transformProps from './transformProps';
|
||||
|
||||
export default createAdaptor(Component, transformProps);
|
|
@ -1,5 +0,0 @@
|
|||
import createAdaptor from '../../utils/createAdaptor';
|
||||
import Component from './ReactSankey';
|
||||
import transformProps from './transformProps';
|
||||
|
||||
export default createAdaptor(Component, transformProps);
|
|
@ -1,5 +0,0 @@
|
|||
import createAdaptor from '../../utils/createAdaptor';
|
||||
import Component from './ReactSunburst';
|
||||
import transformProps from './transformProps';
|
||||
|
||||
export default createAdaptor(Component, transformProps);
|
|
@ -1,5 +0,0 @@
|
|||
import createAdaptor from '../../utils/createAdaptor';
|
||||
import Component from './ReactTable';
|
||||
import transformProps from './transformProps';
|
||||
|
||||
export default createAdaptor(Component, transformProps);
|
|
@ -1,5 +0,0 @@
|
|||
import createAdaptor from '../../utils/createAdaptor';
|
||||
import Component from './TimeTable';
|
||||
import transformProps from './transformProps';
|
||||
|
||||
export default createAdaptor(Component, transformProps);
|
|
@ -48,10 +48,17 @@ const DEFAULT_MARGIN = {
|
|||
left: 0,
|
||||
};
|
||||
|
||||
function clone(children) {
|
||||
return children.map(x => ({
|
||||
...x,
|
||||
children: x.children ? clone(x.children) : null,
|
||||
}));
|
||||
}
|
||||
|
||||
/* Modified from http://bl.ocks.org/ganeshv/6a8e9ada3ab7f2d88022 */
|
||||
function Treemap(element, props) {
|
||||
const {
|
||||
data,
|
||||
data: rawData,
|
||||
width,
|
||||
height,
|
||||
margin = DEFAULT_MARGIN,
|
||||
|
@ -62,6 +69,7 @@ function Treemap(element, props) {
|
|||
const div = d3.select(element);
|
||||
const formatNumber = d3.format(numberFormat);
|
||||
const colorFn = getScale(colorScheme).toFunction();
|
||||
const data = clone(rawData);
|
||||
|
||||
function draw(data, eltWidth, eltHeight) {
|
||||
const navBarHeight = 36;
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
import createAdaptor from '../../utils/createAdaptor';
|
||||
import Component from './ReactTreemap';
|
||||
import transformProps from './transformProps';
|
||||
|
||||
export default createAdaptor(Component, transformProps);
|
|
@ -1,5 +0,0 @@
|
|||
import createAdaptor from '../../utils/createAdaptor';
|
||||
import Component from './ReactWorldMap';
|
||||
import transformProps from './transformProps';
|
||||
|
||||
export default createAdaptor(Component, transformProps);
|
|
@ -0,0 +1,163 @@
|
|||
import React from 'react';
|
||||
import Loadable from 'react-loadable';
|
||||
import PropTypes from 'prop-types';
|
||||
import { createSelector } from 'reselect';
|
||||
import getChartComponentRegistry from '../registries/ChartComponentRegistrySingleton';
|
||||
import getChartTransformPropsRegistry from '../registries/ChartTransformPropsRegistrySingleton';
|
||||
import ChartProps from '../models/ChartProps';
|
||||
|
||||
const IDENTITY = x => x;
|
||||
|
||||
const propTypes = {
|
||||
id: PropTypes.string,
|
||||
className: PropTypes.string,
|
||||
chartProps: PropTypes.instanceOf(ChartProps),
|
||||
chartType: PropTypes.string.isRequired,
|
||||
preTransformProps: PropTypes.func,
|
||||
overrideTransformProps: PropTypes.func,
|
||||
postTransformProps: PropTypes.func,
|
||||
onRenderSuccess: PropTypes.func,
|
||||
onRenderFailure: PropTypes.func,
|
||||
skipRendering: PropTypes.bool,
|
||||
};
|
||||
const defaultProps = {
|
||||
id: '',
|
||||
className: '',
|
||||
preTransformProps: IDENTITY,
|
||||
overrideTransformProps: undefined,
|
||||
postTransformProps: IDENTITY,
|
||||
onRenderSuccess() {},
|
||||
onRenderFailure() {},
|
||||
skipRendering: false,
|
||||
};
|
||||
|
||||
class SuperChart extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.renderChart = this.renderChart.bind(this);
|
||||
this.renderLoading = this.renderLoading.bind(this);
|
||||
|
||||
// memoized function so it will not recompute
|
||||
// and return previous value
|
||||
// unless one of
|
||||
// - preTransformProps
|
||||
// - transformProps
|
||||
// - postTransformProps
|
||||
// - chartProps
|
||||
// is changed.
|
||||
this.processChartProps = createSelector(
|
||||
input => input.preTransformProps,
|
||||
input => input.transformProps,
|
||||
input => input.postTransformProps,
|
||||
input => input.chartProps,
|
||||
(pre, transform, post, chartProps) => post(transform(pre(chartProps))),
|
||||
);
|
||||
|
||||
const componentRegistry = getChartComponentRegistry();
|
||||
const transformPropsRegistry = getChartTransformPropsRegistry();
|
||||
|
||||
// memoized function so it will not recompute
|
||||
// and return previous value
|
||||
// unless one of
|
||||
// - chartType
|
||||
// - overrideTransformProps
|
||||
// is changed.
|
||||
this.createLoadableRenderer = createSelector(
|
||||
input => input.chartType,
|
||||
input => input.overrideTransformProps,
|
||||
(chartType, overrideTransformProps) => {
|
||||
if (chartType) {
|
||||
return Loadable.Map({
|
||||
loader: {
|
||||
Chart: () => componentRegistry.getAsPromise(chartType),
|
||||
transformProps: overrideTransformProps
|
||||
? () => Promise.resolve(overrideTransformProps)
|
||||
: () => transformPropsRegistry.getAsPromise(chartType),
|
||||
},
|
||||
loading: loadingProps => this.renderLoading(loadingProps, chartType),
|
||||
render: this.renderChart,
|
||||
});
|
||||
}
|
||||
return null;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
renderChart(loaded, props) {
|
||||
const Chart = loaded.Chart.default || loaded.Chart;
|
||||
const transformProps = loaded.transformProps;
|
||||
const {
|
||||
chartProps,
|
||||
preTransformProps,
|
||||
postTransformProps,
|
||||
} = props;
|
||||
|
||||
const result = (
|
||||
<Chart
|
||||
{...this.processChartProps({
|
||||
preTransformProps,
|
||||
transformProps,
|
||||
postTransformProps,
|
||||
chartProps,
|
||||
})}
|
||||
/>
|
||||
);
|
||||
setTimeout(() => this.props.onRenderSuccess(), 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
renderLoading(loadableProps, chartType) {
|
||||
const { error } = loadableProps;
|
||||
|
||||
if (error) {
|
||||
const result = (
|
||||
<div className="alert alert-warning" role="alert">
|
||||
<strong>ERROR</strong>
|
||||
<code>chartType="{chartType}"</code> —
|
||||
{JSON.stringify(error)}
|
||||
</div>
|
||||
);
|
||||
setTimeout(() => this.props.onRenderFailure(error), 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
id,
|
||||
className,
|
||||
preTransformProps,
|
||||
postTransformProps,
|
||||
chartProps,
|
||||
skipRendering,
|
||||
} = this.props;
|
||||
|
||||
const LoadableRenderer = this.createLoadableRenderer(this.props);
|
||||
|
||||
// Use this to allow loading the vis components
|
||||
// without rendering (while waiting for data)
|
||||
if (skipRendering || !chartProps) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div id={id} className={className}>
|
||||
{LoadableRenderer && (
|
||||
<LoadableRenderer
|
||||
preTransformProps={preTransformProps}
|
||||
postTransformProps={postTransformProps}
|
||||
chartProps={chartProps}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
SuperChart.propTypes = propTypes;
|
||||
SuperChart.defaultProps = defaultProps;
|
||||
|
||||
export default SuperChart;
|
|
@ -1,3 +1,4 @@
|
|||
import { createSelector } from 'reselect';
|
||||
import convertKeysToCamelCase from '../../../utils/convertKeysToCamelCase';
|
||||
|
||||
export default class ChartProps {
|
||||
|
@ -29,3 +30,44 @@ export default class ChartProps {
|
|||
this.setTooltip = setTooltip;
|
||||
}
|
||||
}
|
||||
|
||||
ChartProps.createSelector = function () {
|
||||
return createSelector(
|
||||
input => input.width,
|
||||
input => input.height,
|
||||
input => input.annotationData,
|
||||
input => input.datasource,
|
||||
input => input.filters,
|
||||
input => input.formData,
|
||||
input => input.onAddFilter,
|
||||
input => input.onError,
|
||||
input => input.payload,
|
||||
input => input.setControlValue,
|
||||
input => input.setTooltip,
|
||||
(
|
||||
width,
|
||||
height,
|
||||
annotationData,
|
||||
datasource,
|
||||
filters,
|
||||
formData,
|
||||
onAddFilter,
|
||||
onError,
|
||||
payload,
|
||||
setControlValue,
|
||||
setTooltip,
|
||||
) => new ChartProps({
|
||||
width,
|
||||
height,
|
||||
annotationData,
|
||||
datasource,
|
||||
filters,
|
||||
formData,
|
||||
onAddFilter,
|
||||
onError,
|
||||
payload,
|
||||
setControlValue,
|
||||
setTooltip,
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,148 +0,0 @@
|
|||
import createAdaptor from '../utils/createAdaptor';
|
||||
import transformProps from './deckgl/transformProps';
|
||||
|
||||
/* eslint-disable global-require */
|
||||
|
||||
// You ***should*** use these to reference viz_types in code
|
||||
export const VIZ_TYPES = {
|
||||
area: 'area',
|
||||
bar: 'bar',
|
||||
big_number: 'big_number',
|
||||
big_number_total: 'big_number_total',
|
||||
box_plot: 'box_plot',
|
||||
bubble: 'bubble',
|
||||
bullet: 'bullet',
|
||||
cal_heatmap: 'cal_heatmap',
|
||||
compare: 'compare',
|
||||
directed_force: 'directed_force',
|
||||
chord: 'chord',
|
||||
dist_bar: 'dist_bar',
|
||||
filter_box: 'filter_box',
|
||||
heatmap: 'heatmap',
|
||||
histogram: 'histogram',
|
||||
horizon: 'horizon',
|
||||
iframe: 'iframe',
|
||||
line: 'line',
|
||||
line_multi: 'line_multi',
|
||||
mapbox: 'mapbox',
|
||||
markup: 'markup',
|
||||
para: 'para',
|
||||
pie: 'pie',
|
||||
pivot_table: 'pivot_table',
|
||||
sankey: 'sankey',
|
||||
separator: 'separator',
|
||||
sunburst: 'sunburst',
|
||||
table: 'table',
|
||||
time_table: 'time_table',
|
||||
time_pivot: 'time_pivot',
|
||||
treemap: 'treemap',
|
||||
country_map: 'country_map',
|
||||
word_cloud: 'word_cloud',
|
||||
world_map: 'world_map',
|
||||
dual_line: 'dual_line',
|
||||
event_flow: 'event_flow',
|
||||
paired_ttest: 'paired_ttest',
|
||||
partition: 'partition',
|
||||
deck_scatter: 'deck_scatter',
|
||||
deck_screengrid: 'deck_screengrid',
|
||||
deck_grid: 'deck_grid',
|
||||
deck_hex: 'deck_hex',
|
||||
deck_path: 'deck_path',
|
||||
deck_geojson: 'deck_geojson',
|
||||
deck_multi: 'deck_multi',
|
||||
deck_arc: 'deck_arc',
|
||||
deck_polygon: 'deck_polygon',
|
||||
rose: 'rose',
|
||||
};
|
||||
|
||||
const loadVis = promise =>
|
||||
promise.then((module) => {
|
||||
const defaultExport = module.default || module;
|
||||
// deckgl visualizations don't use esModules, fix it?
|
||||
return defaultExport.default || defaultExport;
|
||||
});
|
||||
|
||||
const loadDeckGLVis = promise =>
|
||||
loadVis(promise).then(module => createAdaptor(module, transformProps));
|
||||
|
||||
const loadNvd3 = () => loadVis(import(/* webpackChunkName: "nvd3_vis" */ './nvd3/adaptor.jsx'));
|
||||
|
||||
const vizMap = {
|
||||
[VIZ_TYPES.area]: loadNvd3,
|
||||
[VIZ_TYPES.bar]: loadNvd3,
|
||||
[VIZ_TYPES.big_number]: () =>
|
||||
loadVis(import(/* webpackChunkName: 'big_number' */ './BigNumber/adaptor.jsx')),
|
||||
[VIZ_TYPES.big_number_total]: () =>
|
||||
loadVis(import(/* webpackChunkName: "big_number" */ './BigNumber/adaptor.jsx')),
|
||||
[VIZ_TYPES.box_plot]: loadNvd3,
|
||||
[VIZ_TYPES.bubble]: loadNvd3,
|
||||
[VIZ_TYPES.bullet]: loadNvd3,
|
||||
[VIZ_TYPES.cal_heatmap]: () =>
|
||||
loadVis(import(/* webpackChunkName: "cal_heatmap" */ './Calendar/adaptor.jsx')),
|
||||
[VIZ_TYPES.compare]: loadNvd3,
|
||||
[VIZ_TYPES.directed_force]: () =>
|
||||
loadVis(import(/* webpackChunkName: "directed_force" */ './ForceDirected/adaptor.jsx')),
|
||||
[VIZ_TYPES.chord]: () => loadVis(import(/* webpackChunkName: "chord" */ './Chord/adaptor.jsx')),
|
||||
[VIZ_TYPES.dist_bar]: loadNvd3,
|
||||
[VIZ_TYPES.filter_box]: () =>
|
||||
loadVis(import(/* webpackChunkName: "filter_box" */ './FilterBox/adaptor.jsx')),
|
||||
[VIZ_TYPES.heatmap]: () => loadVis(import(/* webpackChunkName: "heatmap" */ './Heatmap/adaptor.jsx')),
|
||||
[VIZ_TYPES.histogram]: () =>
|
||||
loadVis(import(/* webpackChunkName: "histogram" */ './Histogram/adaptor.jsx')),
|
||||
[VIZ_TYPES.horizon]: () => loadVis(import(/* webpackChunkName: "horizon" */ './Horizon/adaptor.jsx')),
|
||||
[VIZ_TYPES.iframe]: () => loadVis(import(/* webpackChunkName: "iframe" */ './iframe.js')),
|
||||
[VIZ_TYPES.line]: loadNvd3,
|
||||
[VIZ_TYPES.line_multi]: () =>
|
||||
loadVis(import(/* webpackChunkName: "line_multi" */ './nvd3/LineMulti/adaptor.jsx')),
|
||||
[VIZ_TYPES.time_pivot]: loadNvd3,
|
||||
[VIZ_TYPES.mapbox]: () => loadVis(import(/* webpackChunkName: "mapbox" */ './MapBox/adaptor.jsx')),
|
||||
[VIZ_TYPES.markup]: () => loadVis(import(/* webpackChunkName: "markup" */ './markup.js')),
|
||||
[VIZ_TYPES.para]: () =>
|
||||
loadVis(import(/* webpackChunkName: "parallel_coordinates" */ './ParallelCoordinates/adaptor.jsx')),
|
||||
[VIZ_TYPES.pie]: loadNvd3,
|
||||
[VIZ_TYPES.pivot_table]: () =>
|
||||
loadVis(import(/* webpackChunkName: "pivot_table" */ './PivotTable/adaptor.jsx')),
|
||||
[VIZ_TYPES.sankey]: () => loadVis(import(/* webpackChunkName: "sankey" */ './Sankey/adaptor.jsx')),
|
||||
[VIZ_TYPES.separator]: () => loadVis(import(/* webpackChunkName: "markup" */ './markup.js')),
|
||||
[VIZ_TYPES.sunburst]: () => loadVis(import(/* webpackChunkName: "sunburst" */ './Sunburst/adaptor.jsx')),
|
||||
[VIZ_TYPES.table]: () => loadVis(import(/* webpackChunkName: "table" */ './Table/adaptor.jsx')),
|
||||
[VIZ_TYPES.time_table]: () =>
|
||||
loadVis(import(/* webpackChunkName: "time_table" */ './TimeTable/adaptor.jsx')),
|
||||
[VIZ_TYPES.treemap]: () => loadVis(import(/* webpackChunkName: "treemap" */ './Treemap/adaptor.jsx')),
|
||||
[VIZ_TYPES.country_map]: () =>
|
||||
loadVis(import(/* webpackChunkName: "country_map" */ './CountryMap/adaptor.jsx')),
|
||||
[VIZ_TYPES.word_cloud]: () =>
|
||||
loadVis(import(/* webpackChunkName: "word_cloud" */ './wordcloud/adaptor.jsx')),
|
||||
[VIZ_TYPES.world_map]: () =>
|
||||
loadVis(import(/* webpackChunkName: "world_map" */ './WorldMap/adaptor.jsx')),
|
||||
[VIZ_TYPES.dual_line]: loadNvd3,
|
||||
[VIZ_TYPES.event_flow]: () =>
|
||||
loadVis(import(/* webpackChunkName: "EventFlow" */ './EventFlow/adaptor.jsx')),
|
||||
[VIZ_TYPES.paired_ttest]: () =>
|
||||
loadVis(import(/* webpackChunkName: "paired_ttest" */ './PairedTTest/adaptor.jsx')),
|
||||
[VIZ_TYPES.partition]: () =>
|
||||
loadVis(import(/* webpackChunkName: "partition" */ './Partition/adaptor.jsx')),
|
||||
[VIZ_TYPES.deck_scatter]: () =>
|
||||
loadDeckGLVis(import(/* webpackChunkName: "deckgl/layers/scatter" */ './deckgl/layers/Scatter/Scatter.jsx')),
|
||||
[VIZ_TYPES.deck_screengrid]: () =>
|
||||
loadDeckGLVis(
|
||||
import(/* webpackChunkName: "deckgl/layers/screengrid" */ './deckgl/layers/Screengrid/Screengrid.jsx'),
|
||||
),
|
||||
[VIZ_TYPES.deck_grid]: () =>
|
||||
loadDeckGLVis(import(/* webpackChunkName: "deckgl/layers/grid" */ './deckgl/layers/Grid/Grid.jsx')),
|
||||
[VIZ_TYPES.deck_hex]: () =>
|
||||
loadDeckGLVis(import(/* webpackChunkName: "deckgl/layers/hex" */ './deckgl/layers/Hex/Hex.jsx')),
|
||||
[VIZ_TYPES.deck_path]: () =>
|
||||
loadDeckGLVis(import(/* webpackChunkName: "deckgl/layers/path" */ './deckgl/layers/Path/Path.jsx')),
|
||||
[VIZ_TYPES.deck_geojson]: () =>
|
||||
loadDeckGLVis(import(/* webpackChunkName: "deckgl/layers/geojson" */ './deckgl/layers/Geojson/Geojson.jsx')),
|
||||
[VIZ_TYPES.deck_arc]: () =>
|
||||
loadDeckGLVis(import(/* webpackChunkName: "deckgl/layers/arc" */ './deckgl/layers/Arc/Arc.jsx')),
|
||||
[VIZ_TYPES.deck_polygon]: () =>
|
||||
loadDeckGLVis(import(/* webpackChunkName: "deckgl/layers/polygon" */ './deckgl/layers/Polygon/Polygon.jsx')),
|
||||
[VIZ_TYPES.deck_multi]: () =>
|
||||
loadDeckGLVis(import(/* webpackChunkName: "deckgl/multi" */ './deckgl/Multi/Multi.jsx')),
|
||||
[VIZ_TYPES.rose]: () => loadVis(import(/* webpackChunkName: "rose" */ './Rose/adaptor.jsx')),
|
||||
};
|
||||
|
||||
export default vizMap;
|
|
@ -7,7 +7,7 @@ import PropTypes from 'prop-types';
|
|||
import 'nvd3/build/nv.d3.min.css';
|
||||
|
||||
import { t } from '../../locales';
|
||||
import AnnotationTypes, { applyNativeColumns } from '../../modules/AnnotationTypes';
|
||||
import ANNOTATION_TYPES, { applyNativeColumns } from '../../modules/AnnotationTypes';
|
||||
import { getScale, getColor } from '../../modules/colors/CategoricalColorNamespace';
|
||||
import { formatDateVerbose } from '../../modules/dates';
|
||||
import { d3TimeFormatPreset, d3FormatPreset } from '../../modules/utils';
|
||||
|
@ -646,7 +646,7 @@ function nvd3Vis(element, props) {
|
|||
// Time series annotations add additional data
|
||||
const timeSeriesAnnotations = annotationLayers
|
||||
.filter(layer => layer.show)
|
||||
.filter(layer => layer.annotationType === AnnotationTypes.TIME_SERIES)
|
||||
.filter(layer => layer.annotationType === ANNOTATION_TYPES.TIME_SERIES)
|
||||
.reduce((bushel, a) =>
|
||||
bushel.concat((annotationData[a.name] || []).map((series) => {
|
||||
if (!series) {
|
||||
|
@ -680,7 +680,7 @@ function nvd3Vis(element, props) {
|
|||
if (isTimeSeries && annotationLayers.length > 0) {
|
||||
// Formula annotations
|
||||
const formulas = annotationLayers
|
||||
.filter(a => a.annotationType === AnnotationTypes.FORMULA)
|
||||
.filter(a => a.annotationType === ANNOTATION_TYPES.FORMULA)
|
||||
.map(a => ({ ...a, formula: mathjs.parse(a.value) }));
|
||||
|
||||
let xMax;
|
||||
|
@ -750,7 +750,7 @@ function nvd3Vis(element, props) {
|
|||
if (annotationData) {
|
||||
// Event annotations
|
||||
annotationLayers.filter(x => (
|
||||
x.annotationType === AnnotationTypes.EVENT &&
|
||||
x.annotationType === ANNOTATION_TYPES.EVENT &&
|
||||
annotationData && annotationData[x.name]
|
||||
)).forEach((config, index) => {
|
||||
const e = applyNativeColumns(config);
|
||||
|
@ -809,7 +809,7 @@ function nvd3Vis(element, props) {
|
|||
|
||||
// Interval annotations
|
||||
annotationLayers.filter(x => (
|
||||
x.annotationType === AnnotationTypes.INTERVAL &&
|
||||
x.annotationType === ANNOTATION_TYPES.INTERVAL &&
|
||||
annotationData && annotationData[x.name]
|
||||
)).forEach((config, index) => {
|
||||
const e = applyNativeColumns(config);
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
import createAdaptor from '../../utils/createAdaptor';
|
||||
import Component from './ReactNVD3';
|
||||
import transformProps from './transformProps';
|
||||
|
||||
export default createAdaptor(Component, transformProps);
|
|
@ -1,5 +0,0 @@
|
|||
import createAdaptor from '../../utils/createAdaptor';
|
||||
import Component from './ReactWordCloud';
|
||||
import transformProps from './transformProps';
|
||||
|
||||
export default createAdaptor(Component, transformProps);
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue