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-dom": "^16.4.1",
|
||||||
"react-gravatar": "^2.6.1",
|
"react-gravatar": "^2.6.1",
|
||||||
"react-hot-loader": "^4.3.6",
|
"react-hot-loader": "^4.3.6",
|
||||||
|
"react-loadable": "^5.5.0",
|
||||||
"react-map-gl": "^3.0.4",
|
"react-map-gl": "^3.0.4",
|
||||||
"react-markdown": "^3.3.0",
|
"react-markdown": "^3.3.0",
|
||||||
"react-redux": "^5.0.2",
|
"react-redux": "^5.0.2",
|
||||||
|
@ -122,6 +123,7 @@
|
||||||
"redux-localstorage": "^0.4.1",
|
"redux-localstorage": "^0.4.1",
|
||||||
"redux-thunk": "^2.1.0",
|
"redux-thunk": "^2.1.0",
|
||||||
"redux-undo": "^1.0.0-beta9-9-7",
|
"redux-undo": "^1.0.0-beta9-9-7",
|
||||||
|
"reselect": "^4.0.0",
|
||||||
"shortid": "^2.2.6",
|
"shortid": "^2.2.6",
|
||||||
"sprintf-js": "^1.1.1",
|
"sprintf-js": "^1.1.1",
|
||||||
"srcdoc-polyfill": "^1.0.0",
|
"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();
|
wrapper.instance().addFilter();
|
||||||
expect(addFilter.callCount).toBe(1);
|
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 $ from 'jquery';
|
||||||
import '../../helpers/shim';
|
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', () => {
|
describe('table viz', () => {
|
||||||
const div = '<div id="slice-container"><div class="dataTables_wrapper"></div></div>';
|
const div = '<div id="slice-container"></div>';
|
||||||
const baseSlice = {
|
const BASE_CHART_PROPS = {
|
||||||
selector: '#slice-container',
|
height: 100,
|
||||||
|
datasource: {
|
||||||
|
verboseMap: {},
|
||||||
|
},
|
||||||
|
filters: {},
|
||||||
formData: {
|
formData: {
|
||||||
metrics: ['count'],
|
metrics: ['count'],
|
||||||
timeseries_limit_metric: null,
|
timeseriesLimitMetric: null,
|
||||||
},
|
},
|
||||||
datasource: {
|
onAddFilter() {},
|
||||||
verbose_map: {},
|
payload: {
|
||||||
},
|
data: {
|
||||||
getFilters: () => ({}),
|
records: [
|
||||||
removeFilter() {},
|
{ gender: 'boy', count: 39245 },
|
||||||
addFilter() {},
|
{ gender: 'girl', count: 36446 },
|
||||||
width: () => 0,
|
],
|
||||||
height: () => 0,
|
columns: ['gender', 'count'],
|
||||||
};
|
},
|
||||||
const basePayload = {
|
|
||||||
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);
|
$('body').html(div);
|
||||||
const container = $(baseSlice.selector);
|
container = document.getElementById('slice-container');
|
||||||
expect(container).toHaveLength(1);
|
$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', () => {
|
it('renders header and body datatables in container', () => {
|
||||||
$('body').html(div);
|
expect($container.find('.dataTable')).toHaveLength(0);
|
||||||
const container = $(baseSlice.selector);
|
Table(container, transformProps(BASE_CHART_PROPS));
|
||||||
|
expect($container.find('.dataTable')).toHaveLength(2);
|
||||||
|
|
||||||
expect(container.find('.dataTable')).toHaveLength(0);
|
const tableHeader = $container.find('.dataTable')[0];
|
||||||
tableVis(baseSlice, basePayload);
|
|
||||||
expect(container.find('.dataTable')).toHaveLength(2);
|
|
||||||
|
|
||||||
const tableHeader = container.find('.dataTable')[0];
|
|
||||||
expect($(tableHeader).find('thead tr')).toHaveLength(1);
|
expect($(tableHeader).find('thead tr')).toHaveLength(1);
|
||||||
expect($(tableHeader).find('th')).toHaveLength(2);
|
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('tbody tr')).toHaveLength(2);
|
||||||
expect($(tableBody).find('th')).toHaveLength(2);
|
expect($(tableBody).find('th')).toHaveLength(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('hides the sort by column', () => {
|
it('hides the sort by column', () => {
|
||||||
$('body').html(div);
|
const chartProps = {
|
||||||
const slice = { ...baseSlice };
|
...BASE_CHART_PROPS,
|
||||||
slice.formData = { ...baseSlice.formData,
|
formData: {
|
||||||
timeseries_limit_metric: {
|
...BASE_CHART_PROPS.formData,
|
||||||
label: 'SUM(sum_boys)',
|
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);
|
Table(container, transformProps(chartProps));
|
||||||
const tableHeader = container.find('.dataTable')[0];
|
const tableHeader = $container.find('.dataTable')[0];
|
||||||
expect($(tableHeader).find('th')).toHaveLength(2);
|
expect($(tableHeader).find('th')).toHaveLength(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('works with empty list for sort by', () => {
|
it('works with empty list for sort by', () => {
|
||||||
$('body').html(div);
|
const chartProps = {
|
||||||
const slice = { ...baseSlice };
|
...BASE_CHART_PROPS,
|
||||||
slice.formData = { ...baseSlice.formData,
|
formData: {
|
||||||
timeseries_limit_metric: [],
|
...BASE_CHART_PROPS.formData,
|
||||||
};
|
timeseriesLimitMetric: [],
|
||||||
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)'],
|
|
||||||
},
|
},
|
||||||
|
payload: PAYLOAD2,
|
||||||
};
|
};
|
||||||
tableVis(slice, payload);
|
|
||||||
|
|
||||||
const container = $(slice.selector);
|
Table(container, transformProps(chartProps));
|
||||||
const tableBody = container.find('.dataTable')[1];
|
const tableBody = $container.find('.dataTable')[1];
|
||||||
expect($(tableBody).find('th')).toHaveLength(3);
|
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 dompurify from 'dompurify';
|
||||||
|
import { snakeCase } from 'lodash';
|
||||||
import ChartBody from './ChartBody';
|
import PropTypes from 'prop-types';
|
||||||
import Loading from '../components/Loading';
|
import React from 'react';
|
||||||
|
import { Tooltip } from 'react-bootstrap';
|
||||||
import { Logger, LOG_ACTIONS_RENDER_CHART } from '../logger';
|
import { Logger, LOG_ACTIONS_RENDER_CHART } from '../logger';
|
||||||
import StackTraceMessage from '../components/StackTraceMessage';
|
import Loading from '../components/Loading';
|
||||||
import RefreshChartOverlay from '../components/RefreshChartOverlay';
|
import RefreshChartOverlay from '../components/RefreshChartOverlay';
|
||||||
import visPromiseLookup from '../visualizations';
|
import StackTraceMessage from '../components/StackTraceMessage';
|
||||||
import sandboxedEval from '../modules/sandbox';
|
import ChartProps from '../visualizations/core/models/ChartProps';
|
||||||
|
import SuperChart from '../visualizations/core/components/SuperChart';
|
||||||
import './chart.css';
|
import './chart.css';
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
annotationData: PropTypes.object,
|
annotationData: PropTypes.object,
|
||||||
actions: PropTypes.object,
|
actions: PropTypes.object,
|
||||||
chartId: PropTypes.number.isRequired,
|
chartId: PropTypes.number.isRequired,
|
||||||
containerId: PropTypes.string.isRequired,
|
|
||||||
datasource: PropTypes.object.isRequired,
|
datasource: PropTypes.object.isRequired,
|
||||||
|
filters: PropTypes.object,
|
||||||
formData: PropTypes.object.isRequired,
|
formData: PropTypes.object.isRequired,
|
||||||
headerHeight: PropTypes.number,
|
|
||||||
height: PropTypes.number,
|
height: PropTypes.number,
|
||||||
width: PropTypes.number,
|
width: PropTypes.number,
|
||||||
setControlValue: PropTypes.func,
|
setControlValue: PropTypes.func,
|
||||||
|
@ -28,46 +26,34 @@ const propTypes = {
|
||||||
// state
|
// state
|
||||||
chartAlert: PropTypes.string,
|
chartAlert: PropTypes.string,
|
||||||
chartStatus: PropTypes.string,
|
chartStatus: PropTypes.string,
|
||||||
chartUpdateEndTime: PropTypes.number,
|
|
||||||
chartUpdateStartTime: PropTypes.number,
|
|
||||||
latestQueryFormData: PropTypes.object,
|
|
||||||
queryResponse: PropTypes.object,
|
queryResponse: PropTypes.object,
|
||||||
lastRendered: PropTypes.number,
|
|
||||||
triggerQuery: PropTypes.bool,
|
triggerQuery: PropTypes.bool,
|
||||||
refreshOverlayVisible: PropTypes.bool,
|
refreshOverlayVisible: PropTypes.bool,
|
||||||
errorMessage: PropTypes.node,
|
errorMessage: PropTypes.node,
|
||||||
// dashboard callbacks
|
// dashboard callbacks
|
||||||
addFilter: PropTypes.func,
|
addFilter: PropTypes.func,
|
||||||
getFilters: PropTypes.func,
|
|
||||||
onQuery: PropTypes.func,
|
onQuery: PropTypes.func,
|
||||||
onDismissRefreshOverlay: PropTypes.func,
|
onDismissRefreshOverlay: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const BLANK = {};
|
||||||
|
|
||||||
const defaultProps = {
|
const defaultProps = {
|
||||||
addFilter: () => ({}),
|
addFilter: () => BLANK,
|
||||||
getFilters: () => ({}),
|
filters: BLANK,
|
||||||
|
setControlValue() {},
|
||||||
};
|
};
|
||||||
|
|
||||||
class Chart extends React.PureComponent {
|
class Chart extends React.PureComponent {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
// visualizations are lazy-loaded with promises that resolve to a renderVis function
|
this.state = {};
|
||||||
this.state = {
|
|
||||||
renderVis: null,
|
|
||||||
};
|
|
||||||
|
|
||||||
// these properties are used by visualizations
|
this.createChartProps = ChartProps.createSelector();
|
||||||
this.annotationData = props.annotationData;
|
this.handleAddFilter = this.handleAddFilter.bind(this);
|
||||||
this.containerId = props.containerId;
|
this.handleRenderSuccess = this.handleRenderSuccess.bind(this);
|
||||||
this.selector = `#${this.containerId}`;
|
this.handleRenderFailure = this.handleRenderFailure.bind(this);
|
||||||
this.formData = props.formData;
|
this.setTooltip = this.setTooltip.bind(this);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
@ -78,110 +64,82 @@ class Chart extends React.PureComponent {
|
||||||
this.props.timeout,
|
this.props.timeout,
|
||||||
this.props.chartId,
|
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) {
|
setTooltip(tooltip) {
|
||||||
this.setState({ tooltip });
|
this.setState({ tooltip });
|
||||||
}
|
}
|
||||||
|
|
||||||
loadAsyncVis(visType) {
|
handleAddFilter(col, vals, merge = true, refresh = true) {
|
||||||
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) {
|
|
||||||
this.props.addFilter(col, vals, merge, refresh);
|
this.props.addFilter(col, vals, merge, refresh);
|
||||||
}
|
}
|
||||||
|
|
||||||
clearError() {
|
handleRenderSuccess() {
|
||||||
this.setState({ errorMsg: null });
|
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() {
|
handleRenderFailure(e) {
|
||||||
return this.props.width;
|
const { actions, chartId } = this.props;
|
||||||
|
console.warn(e); // eslint-disable-line
|
||||||
|
actions.chartRenderingFailed(e, chartId);
|
||||||
}
|
}
|
||||||
|
|
||||||
headerHeight() {
|
prepareChartProps() {
|
||||||
return this.props.headerHeight || 0;
|
const {
|
||||||
}
|
width,
|
||||||
|
height,
|
||||||
|
annotationData,
|
||||||
|
datasource,
|
||||||
|
filters,
|
||||||
|
formData,
|
||||||
|
queryResponse,
|
||||||
|
setControlValue,
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
height() {
|
return this.createChartProps({
|
||||||
return this.props.height;
|
width,
|
||||||
}
|
height,
|
||||||
|
annotationData,
|
||||||
error(e) {
|
datasource,
|
||||||
this.props.actions.chartRenderingFailed(e, this.props.chartId);
|
filters,
|
||||||
|
formData,
|
||||||
|
onAddFilter: this.handleAddFilter,
|
||||||
|
onError: this.handleRenderFailure,
|
||||||
|
payload: queryResponse,
|
||||||
|
setControlValue,
|
||||||
|
setTooltip: this.setTooltip,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
renderTooltip() {
|
renderTooltip() {
|
||||||
if (this.state.tooltip) {
|
const { tooltip } = this.state;
|
||||||
|
|
||||||
|
if (tooltip) {
|
||||||
return (
|
return (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
className="chart-tooltip"
|
className="chart-tooltip"
|
||||||
id="chart-tooltip"
|
id="chart-tooltip"
|
||||||
placement="right"
|
placement="right"
|
||||||
positionTop={this.state.tooltip.y + 30}
|
positionTop={tooltip.y + 30}
|
||||||
positionLeft={this.state.tooltip.x + 30}
|
positionLeft={tooltip.x + 30}
|
||||||
arrowOffsetTop={10}
|
arrowOffsetTop={10}
|
||||||
>
|
>
|
||||||
{typeof (this.state.tooltip.content) === 'string' ?
|
{typeof (tooltip.content) === 'string' ?
|
||||||
<div // eslint-disable-next-line react/no-danger
|
<div // eslint-disable-next-line react/no-danger
|
||||||
dangerouslySetInnerHTML={{ __html: dompurify.sanitize(this.state.tooltip.content) }}
|
dangerouslySetInnerHTML={{ __html: dompurify.sanitize(tooltip.content) }}
|
||||||
/>
|
/>
|
||||||
:
|
: tooltip.content
|
||||||
this.state.tooltip.content
|
|
||||||
}
|
}
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
|
@ -189,85 +147,61 @@ class Chart extends React.PureComponent {
|
||||||
return null;
|
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() {
|
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
|
// 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 (
|
return (
|
||||||
<div className={`chart-container ${isLoading ? 'is-loading' : ''}`} style={containerStyles}>
|
<div
|
||||||
|
className={`chart-container ${isLoading ? 'is-loading' : ''}`}
|
||||||
|
style={containerStyles}
|
||||||
|
>
|
||||||
{this.renderTooltip()}
|
{this.renderTooltip()}
|
||||||
|
|
||||||
{isLoading && <Loading size={50} />}
|
{isLoading && <Loading size={50} />}
|
||||||
|
|
||||||
{this.props.chartAlert && (
|
{chartAlert && (
|
||||||
<StackTraceMessage
|
<StackTraceMessage
|
||||||
message={this.props.chartAlert}
|
message={chartAlert}
|
||||||
queryResponse={this.props.queryResponse}
|
queryResponse={queryResponse}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!isLoading &&
|
{!isLoading && !chartAlert && isFaded && (
|
||||||
!this.props.chartAlert &&
|
<RefreshChartOverlay
|
||||||
this.props.refreshOverlayVisible &&
|
width={width}
|
||||||
!this.props.errorMessage &&
|
height={height}
|
||||||
this.container && (
|
onQuery={onQuery}
|
||||||
<RefreshChartOverlay
|
onDismiss={onDismissRefreshOverlay}
|
||||||
height={this.height()}
|
/>
|
||||||
width={this.width()}
|
)}
|
||||||
onQuery={this.props.onQuery}
|
|
||||||
onDismiss={this.props.onDismissRefreshOverlay}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{!isLoading &&
|
<SuperChart
|
||||||
!this.props.chartAlert && (
|
className={`slice_container ${snakeCase(vizType)} ${isFaded ? ' faded' : ''}`}
|
||||||
<ChartBody
|
chartType={vizType}
|
||||||
containerId={this.containerId}
|
chartProps={skipChartRendering ? null : this.prepareChartProps()}
|
||||||
vizType={this.props.vizType}
|
onRenderSuccess={this.handleRenderSuccess}
|
||||||
height={this.height}
|
onRenderFailure={this.handleRenderFailure}
|
||||||
width={this.width}
|
skipRendering={skipChartRendering}
|
||||||
faded={
|
/>
|
||||||
this.props.refreshOverlayVisible && !this.props.errorMessage
|
|
||||||
}
|
|
||||||
ref={(inner) => {
|
|
||||||
this.container = inner;
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
</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 { toggleCheckbox } from './modules/utils';
|
||||||
import setupClient from './setup/setupClient';
|
import setupClient from './setup/setupClient';
|
||||||
import setupColors from './setup/setupColors';
|
import setupColors from './setup/setupColors';
|
||||||
|
import setupPlugins from './setup/setupPlugins';
|
||||||
|
|
||||||
|
setupColors();
|
||||||
|
setupPlugins();
|
||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
$(':checkbox[data-checkbox-api-prefix]').change(function () {
|
$(':checkbox[data-checkbox-api-prefix]').change(function () {
|
||||||
|
@ -29,7 +33,6 @@ $(document).ready(function () {
|
||||||
|
|
||||||
export function appSetup() {
|
export function appSetup() {
|
||||||
setupClient();
|
setupClient();
|
||||||
setupColors();
|
|
||||||
|
|
||||||
// A set of hacks to allow apps to run within a FAB template
|
// A set of hacks to allow apps to run within a FAB template
|
||||||
// this allows for the server side generated menus to function
|
// this allows for the server side generated menus to function
|
||||||
|
|
|
@ -1,14 +1,12 @@
|
||||||
import cx from 'classnames';
|
import cx from 'classnames';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { exportChart } from '../../../explore/exploreUtils';
|
import { exportChart } from '../../../explore/exploreUtils';
|
||||||
import SliceHeader from '../SliceHeader';
|
import SliceHeader from '../SliceHeader';
|
||||||
import ChartContainer from '../../../chart/ChartContainer';
|
import ChartContainer from '../../../chart/ChartContainer';
|
||||||
import MissingChart from '../MissingChart';
|
import MissingChart from '../MissingChart';
|
||||||
import { chartPropType } from '../../../chart/chartReducer';
|
import { chartPropType } from '../../../chart/chartReducer';
|
||||||
import { slicePropShape } from '../../util/propShapes';
|
import { slicePropShape } from '../../util/propShapes';
|
||||||
import { VIZ_TYPES } from '../../../visualizations';
|
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
id: PropTypes.number.isRequired,
|
id: PropTypes.number.isRequired,
|
||||||
|
@ -39,7 +37,7 @@ const RESIZE_TIMEOUT = 350;
|
||||||
const SHOULD_UPDATE_ON_PROP_CHANGES = Object.keys(propTypes).filter(
|
const SHOULD_UPDATE_ON_PROP_CHANGES = Object.keys(propTypes).filter(
|
||||||
prop => prop !== 'width' && prop !== 'height',
|
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 {
|
class Chart extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -53,7 +51,6 @@ class Chart extends React.Component {
|
||||||
this.exploreChart = this.exploreChart.bind(this);
|
this.exploreChart = this.exploreChart.bind(this);
|
||||||
this.exportCSV = this.exportCSV.bind(this);
|
this.exportCSV = this.exportCSV.bind(this);
|
||||||
this.forceRefresh = this.forceRefresh.bind(this);
|
this.forceRefresh = this.forceRefresh.bind(this);
|
||||||
this.getFilters = this.getFilters.bind(this);
|
|
||||||
this.resize = this.resize.bind(this);
|
this.resize = this.resize.bind(this);
|
||||||
this.setDescriptionRef = this.setDescriptionRef.bind(this);
|
this.setDescriptionRef = this.setDescriptionRef.bind(this);
|
||||||
this.setHeaderRef = this.setHeaderRef.bind(this);
|
this.setHeaderRef = this.setHeaderRef.bind(this);
|
||||||
|
@ -92,10 +89,6 @@ class Chart extends React.Component {
|
||||||
clearTimeout(this.resizeTimeout);
|
clearTimeout(this.resizeTimeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
getFilters() {
|
|
||||||
return this.props.filters;
|
|
||||||
}
|
|
||||||
|
|
||||||
getChartHeight() {
|
getChartHeight() {
|
||||||
const headerHeight = this.getHeaderHeight();
|
const headerHeight = this.getHeaderHeight();
|
||||||
const descriptionHeight =
|
const descriptionHeight =
|
||||||
|
@ -147,6 +140,7 @@ class Chart extends React.Component {
|
||||||
datasource,
|
datasource,
|
||||||
isExpanded,
|
isExpanded,
|
||||||
editMode,
|
editMode,
|
||||||
|
filters,
|
||||||
formData,
|
formData,
|
||||||
updateSliceName,
|
updateSliceName,
|
||||||
sliceName,
|
sliceName,
|
||||||
|
@ -214,27 +208,20 @@ class Chart extends React.Component {
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<ChartContainer
|
<ChartContainer
|
||||||
containerId={`slice-container-${id}`}
|
|
||||||
chartId={id}
|
|
||||||
datasource={datasource}
|
|
||||||
formData={formData}
|
|
||||||
headerHeight={this.getHeaderHeight()}
|
|
||||||
height={this.getChartHeight()}
|
|
||||||
width={width}
|
width={width}
|
||||||
timeout={timeout}
|
height={this.getChartHeight()}
|
||||||
vizType={slice.viz_type}
|
|
||||||
addFilter={this.addFilter}
|
addFilter={this.addFilter}
|
||||||
getFilters={this.getFilters}
|
|
||||||
annotationData={chart.annotationData}
|
annotationData={chart.annotationData}
|
||||||
chartAlert={chart.chartAlert}
|
chartAlert={chart.chartAlert}
|
||||||
|
chartId={id}
|
||||||
chartStatus={chart.chartStatus}
|
chartStatus={chart.chartStatus}
|
||||||
chartUpdateEndTime={chart.chartUpdateEndTime}
|
datasource={datasource}
|
||||||
chartUpdateStartTime={chart.chartUpdateStartTime}
|
filters={filters}
|
||||||
latestQueryFormData={chart.latestQueryFormData}
|
formData={formData}
|
||||||
lastRendered={chart.lastRendered}
|
|
||||||
queryResponse={chart.queryResponse}
|
queryResponse={chart.queryResponse}
|
||||||
queryController={chart.queryController}
|
timeout={timeout}
|
||||||
triggerQuery={chart.triggerQuery}
|
triggerQuery={chart.triggerQuery}
|
||||||
|
vizType={slice.viz_type}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -39,30 +39,23 @@ class ExploreChartPanel extends React.PureComponent {
|
||||||
<ParentSize>
|
<ParentSize>
|
||||||
{({ width, height }) => (width > 0 && height > 0) && (
|
{({ width, height }) => (width > 0 && height > 0) && (
|
||||||
<ChartContainer
|
<ChartContainer
|
||||||
chartId={chart.id}
|
|
||||||
containerId={this.props.containerId}
|
|
||||||
datasource={this.props.datasource}
|
|
||||||
formData={this.props.form_data}
|
|
||||||
width={Math.floor(width)}
|
width={Math.floor(width)}
|
||||||
height={parseInt(this.props.height, 10) - headerHeight}
|
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}
|
annotationData={chart.annotationData}
|
||||||
chartAlert={chart.chartAlert}
|
chartAlert={chart.chartAlert}
|
||||||
|
chartId={chart.id}
|
||||||
chartStatus={chart.chartStatus}
|
chartStatus={chart.chartStatus}
|
||||||
chartUpdateEndTime={chart.chartUpdateEndTime}
|
datasource={this.props.datasource}
|
||||||
chartUpdateStartTime={chart.chartUpdateStartTime}
|
errorMessage={this.props.errorMessage}
|
||||||
latestQueryFormData={chart.latestQueryFormData}
|
formData={this.props.form_data}
|
||||||
lastRendered={chart.lastRendered}
|
onDismissRefreshOverlay={this.props.onDismissRefreshOverlay}
|
||||||
|
onQuery={this.props.onQuery}
|
||||||
queryResponse={chart.queryResponse}
|
queryResponse={chart.queryResponse}
|
||||||
queryController={chart.queryController}
|
refreshOverlayVisible={this.props.refreshOverlayVisible}
|
||||||
|
setControlValue={this.props.actions.setControlValue}
|
||||||
|
timeout={this.props.timeout}
|
||||||
triggerQuery={chart.triggerQuery}
|
triggerQuery={chart.triggerQuery}
|
||||||
|
vizType={this.props.vizType}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</ParentSize>
|
</ParentSize>
|
||||||
|
|
|
@ -9,20 +9,18 @@ import SelectControl from './SelectControl';
|
||||||
import TextControl from './TextControl';
|
import TextControl from './TextControl';
|
||||||
import CheckboxControl from './CheckboxControl';
|
import CheckboxControl from './CheckboxControl';
|
||||||
|
|
||||||
import AnnotationTypes, {
|
import ANNOTATION_TYPES, {
|
||||||
DEFAULT_ANNOTATION_TYPE,
|
|
||||||
ANNOTATION_SOURCE_TYPES,
|
ANNOTATION_SOURCE_TYPES,
|
||||||
getAnnotationSourceTypeLabels,
|
ANNOTATION_TYPES_METADATA,
|
||||||
getAnnotationTypeLabel,
|
DEFAULT_ANNOTATION_TYPE,
|
||||||
getSupportedSourceTypes,
|
|
||||||
getSupportedAnnotationTypes,
|
|
||||||
requiresQuery,
|
requiresQuery,
|
||||||
|
ANNOTATION_SOURCE_TYPES_METADATA,
|
||||||
} from '../../../modules/AnnotationTypes';
|
} from '../../../modules/AnnotationTypes';
|
||||||
|
|
||||||
import PopoverSection from '../../../components/PopoverSection';
|
import PopoverSection from '../../../components/PopoverSection';
|
||||||
import ControlHeader from '../ControlHeader';
|
import ControlHeader from '../ControlHeader';
|
||||||
import { nonEmpty } from '../../validators';
|
import { nonEmpty } from '../../validators';
|
||||||
import vizTypes from '../../visTypes';
|
import getChartMetadataRegistry from '../../../visualizations/core/registries/ChartMetadataRegistrySingleton';
|
||||||
|
|
||||||
import { t } from '../../../locales';
|
import { t } from '../../../locales';
|
||||||
import getCategoricalSchemeRegistry from '../../../modules/colors/CategoricalSchemeRegistrySingleton';
|
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) {
|
isValidFormula(value, annotationType) {
|
||||||
if (annotationType === AnnotationTypes.FORMULA) {
|
if (annotationType === ANNOTATION_TYPES.FORMULA) {
|
||||||
try {
|
try {
|
||||||
mathjs
|
mathjs
|
||||||
.parse(value)
|
.parse(value)
|
||||||
|
@ -166,10 +179,10 @@ export default class AnnotationLayer extends React.PureComponent {
|
||||||
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)];
|
const errors = [nonEmpty(name), nonEmpty(annotationType), nonEmpty(value)];
|
||||||
if (sourceType !== ANNOTATION_SOURCE_TYPES.NATIVE) {
|
if (sourceType !== ANNOTATION_SOURCE_TYPES.NATIVE) {
|
||||||
if (annotationType === AnnotationTypes.EVENT) {
|
if (annotationType === ANNOTATION_TYPES.EVENT) {
|
||||||
errors.push(nonEmpty(timeColumn));
|
errors.push(nonEmpty(timeColumn));
|
||||||
}
|
}
|
||||||
if (annotationType === AnnotationTypes.INTERVAL) {
|
if (annotationType === ANNOTATION_TYPES.INTERVAL) {
|
||||||
errors.push(nonEmpty(timeColumn));
|
errors.push(nonEmpty(timeColumn));
|
||||||
errors.push(nonEmpty(intervalEndColumn));
|
errors.push(nonEmpty(intervalEndColumn));
|
||||||
}
|
}
|
||||||
|
@ -223,14 +236,18 @@ export default class AnnotationLayer extends React.PureComponent {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else if (requiresQuery(sourceType)) {
|
} 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({
|
this.setState({
|
||||||
isLoadingOptions: false,
|
isLoadingOptions: false,
|
||||||
valueOptions: json
|
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 })),
|
.map(x => ({ value: x.id, label: x.title, slice: x })),
|
||||||
}),
|
});
|
||||||
);
|
});
|
||||||
} else {
|
} else {
|
||||||
this.setState({
|
this.setState({
|
||||||
isLoadingOptions: false,
|
isLoadingOptions: false,
|
||||||
|
@ -282,11 +299,11 @@ export default class AnnotationLayer extends React.PureComponent {
|
||||||
label = label = t('Chart');
|
label = label = t('Chart');
|
||||||
description = `Use a pre defined Superset Chart as a source for annotations and overlays.
|
description = `Use a pre defined Superset Chart as a source for annotations and overlays.
|
||||||
your chart must be one of these visualization types:
|
your chart must be one of these visualization types:
|
||||||
[${getSupportedSourceTypes(annotationType)
|
[${this.getSupportedSourceTypes(annotationType)
|
||||||
.map(x => (x in vizTypes && 'label' in vizTypes[x] ? vizTypes[x].label : ''))
|
.map(x => x.label)
|
||||||
.join(', ')}]`;
|
.join(', ')}]`;
|
||||||
}
|
}
|
||||||
} else if (annotationType === AnnotationTypes.FORMULA) {
|
} else if (annotationType === ANNOTATION_TYPES.FORMULA) {
|
||||||
label = 'Formula';
|
label = 'Formula';
|
||||||
description = `Expects a formula with depending time parameter 'x'
|
description = `Expects a formula with depending time parameter 'x'
|
||||||
in milliseconds since epoch. mathjs is used to evaluate the formulas.
|
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 (
|
return (
|
||||||
<TextControl
|
<TextControl
|
||||||
name="annotation-layer-value"
|
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
|
info={`This section allows you to configure how to use the slice
|
||||||
to generate annotations.`}
|
to generate annotations.`}
|
||||||
>
|
>
|
||||||
{(annotationType === AnnotationTypes.EVENT ||
|
{(annotationType === ANNOTATION_TYPES.EVENT ||
|
||||||
annotationType === AnnotationTypes.INTERVAL) && (
|
annotationType === ANNOTATION_TYPES.INTERVAL) && (
|
||||||
<SelectControl
|
<SelectControl
|
||||||
hovered
|
hovered
|
||||||
name="annotation-layer-time-column"
|
name="annotation-layer-time-column"
|
||||||
label={
|
label={
|
||||||
annotationType === AnnotationTypes.INTERVAL
|
annotationType === ANNOTATION_TYPES.INTERVAL
|
||||||
? 'Interval Start column'
|
? 'Interval Start column'
|
||||||
: 'Event Time Column'
|
: 'Event Time Column'
|
||||||
}
|
}
|
||||||
|
@ -374,7 +391,7 @@ export default class AnnotationLayer extends React.PureComponent {
|
||||||
onChange={v => this.setState({ timeColumn: v })}
|
onChange={v => this.setState({ timeColumn: v })}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{annotationType === AnnotationTypes.INTERVAL && (
|
{annotationType === ANNOTATION_TYPES.INTERVAL && (
|
||||||
<SelectControl
|
<SelectControl
|
||||||
hovered
|
hovered
|
||||||
name="annotation-layer-intervalEnd"
|
name="annotation-layer-intervalEnd"
|
||||||
|
@ -395,7 +412,7 @@ export default class AnnotationLayer extends React.PureComponent {
|
||||||
value={titleColumn}
|
value={titleColumn}
|
||||||
onChange={v => this.setState({ titleColumn: v })}
|
onChange={v => this.setState({ titleColumn: v })}
|
||||||
/>
|
/>
|
||||||
{annotationType !== AnnotationTypes.TIME_SERIES && (
|
{annotationType !== ANNOTATION_TYPES.TIME_SERIES && (
|
||||||
<SelectControl
|
<SelectControl
|
||||||
hovered
|
hovered
|
||||||
name="annotation-layer-title"
|
name="annotation-layer-title"
|
||||||
|
@ -553,7 +570,7 @@ export default class AnnotationLayer extends React.PureComponent {
|
||||||
value={width}
|
value={width}
|
||||||
onChange={v => this.setState({ width: v })}
|
onChange={v => this.setState({ width: v })}
|
||||||
/>
|
/>
|
||||||
{annotationType === AnnotationTypes.TIME_SERIES && (
|
{annotationType === ANNOTATION_TYPES.TIME_SERIES && (
|
||||||
<CheckboxControl
|
<CheckboxControl
|
||||||
hovered
|
hovered
|
||||||
name="annotation-layer-show-markers"
|
name="annotation-layer-show-markers"
|
||||||
|
@ -563,7 +580,7 @@ export default class AnnotationLayer extends React.PureComponent {
|
||||||
onChange={v => this.setState({ showMarkers: v })}
|
onChange={v => this.setState({ showMarkers: v })}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{annotationType === AnnotationTypes.TIME_SERIES && (
|
{annotationType === ANNOTATION_TYPES.TIME_SERIES && (
|
||||||
<CheckboxControl
|
<CheckboxControl
|
||||||
hovered
|
hovered
|
||||||
name="annotation-layer-hide-line"
|
name="annotation-layer-hide-line"
|
||||||
|
@ -580,6 +597,13 @@ export default class AnnotationLayer extends React.PureComponent {
|
||||||
render() {
|
render() {
|
||||||
const { isNew, name, annotationType, sourceType, show } = this.state;
|
const { isNew, name, annotationType, sourceType, show } = this.state;
|
||||||
const isValid = this.isValidForm();
|
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 (
|
return (
|
||||||
<div>
|
<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>}
|
||||||
|
@ -610,23 +634,17 @@ export default class AnnotationLayer extends React.PureComponent {
|
||||||
description={t('Choose the Annotation Layer Type')}
|
description={t('Choose the Annotation Layer Type')}
|
||||||
label={t('Annotation Layer Type')}
|
label={t('Annotation Layer Type')}
|
||||||
name="annotation-layer-type"
|
name="annotation-layer-type"
|
||||||
options={getSupportedAnnotationTypes(this.props.vizType).map(x => ({
|
options={supportedAnnotationTypes}
|
||||||
value: x,
|
|
||||||
label: getAnnotationTypeLabel(x),
|
|
||||||
}))}
|
|
||||||
value={annotationType}
|
value={annotationType}
|
||||||
onChange={this.handleAnnotationType}
|
onChange={this.handleAnnotationType}
|
||||||
/>
|
/>
|
||||||
{!!getSupportedSourceTypes(annotationType).length && (
|
{!!supportedSourceTypes.length && (
|
||||||
<SelectControl
|
<SelectControl
|
||||||
hovered
|
hovered
|
||||||
description="Choose the source of your annotations"
|
description="Choose the source of your annotations"
|
||||||
label="Annotation Source"
|
label="Annotation Source"
|
||||||
name="annotation-source-type"
|
name="annotation-source-type"
|
||||||
options={getSupportedSourceTypes(annotationType).map(x => ({
|
options={supportedSourceTypes}
|
||||||
value: x,
|
|
||||||
label: getAnnotationSourceTypeLabels(x),
|
|
||||||
}))}
|
|
||||||
value={sourceType}
|
value={sourceType}
|
||||||
onChange={this.handleAnnotationSourceType}
|
onChange={this.handleAnnotationSourceType}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1,81 +1,50 @@
|
||||||
import { VIZ_TYPES } from '../visualizations';
|
function extractTypes(metadata) {
|
||||||
import vizTypes from '../explore/visTypes';
|
return Object.keys(metadata)
|
||||||
|
.reduce((prev, key) => {
|
||||||
export const ANNOTATION_TYPES = {
|
const result = prev;
|
||||||
FORMULA: 'FORMULA',
|
result[key] = key;
|
||||||
EVENT: 'EVENT',
|
return result;
|
||||||
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];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 DEFAULT_ANNOTATION_TYPE = ANNOTATION_TYPES.FORMULA;
|
||||||
|
|
||||||
export const ANNOTATION_SOURCE_TYPES = {
|
export const ANNOTATION_SOURCE_TYPES_METADATA = {
|
||||||
NATIVE: 'NATIVE',
|
NATIVE: {
|
||||||
...VIZ_TYPES,
|
value: 'NATIVE',
|
||||||
|
label: 'Superset annotation',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export function getAnnotationSourceTypeLabels(sourceType) {
|
export const ANNOTATION_SOURCE_TYPES = extractTypes(ANNOTATION_SOURCE_TYPES_METADATA);
|
||||||
return ANNOTATION_SOURCE_TYPES.NATIVE === sourceType ? 'Superset annotation' :
|
|
||||||
vizTypes[sourceType].label;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function requiresQuery(annotationSourceType) {
|
export function requiresQuery(annotationSourceType) {
|
||||||
return !!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 = {
|
const NATIVE_COLUMN_NAMES = {
|
||||||
timeColumn: 'start_dttm',
|
timeColumn: 'start_dttm',
|
||||||
intervalEndColumn: 'end_dttm',
|
intervalEndColumn: 'end_dttm',
|
||||||
|
@ -91,4 +60,3 @@ export function applyNativeColumns(annotation) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ANNOTATION_TYPES;
|
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) {
|
export function formatSelectOptionsForRange(start, end) {
|
||||||
// outputs array of arrays
|
// outputs array of arrays
|
||||||
// formatSelectOptionsForRange(1, 5)
|
// 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;
|
padding: 10px;
|
||||||
position: static !important;
|
position: static !important;
|
||||||
overflow: auto !important;
|
overflow: auto !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cal_heatmap .slice_container .ch-tooltip {
|
.cal_heatmap .ch-tooltip {
|
||||||
margin-left: 20px;
|
margin-left: 20px;
|
||||||
margin-top: 5px;
|
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-width: 1px;
|
||||||
border-color: transparent;
|
border-color: transparent;
|
||||||
}
|
}
|
||||||
.filter_box.slice_container {
|
.filter_box {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
overflow: visible !important;
|
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,
|
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 */
|
/* Modified from http://bl.ocks.org/ganeshv/6a8e9ada3ab7f2d88022 */
|
||||||
function Treemap(element, props) {
|
function Treemap(element, props) {
|
||||||
const {
|
const {
|
||||||
data,
|
data: rawData,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
margin = DEFAULT_MARGIN,
|
margin = DEFAULT_MARGIN,
|
||||||
|
@ -62,6 +69,7 @@ function Treemap(element, props) {
|
||||||
const div = d3.select(element);
|
const div = d3.select(element);
|
||||||
const formatNumber = d3.format(numberFormat);
|
const formatNumber = d3.format(numberFormat);
|
||||||
const colorFn = getScale(colorScheme).toFunction();
|
const colorFn = getScale(colorScheme).toFunction();
|
||||||
|
const data = clone(rawData);
|
||||||
|
|
||||||
function draw(data, eltWidth, eltHeight) {
|
function draw(data, eltWidth, eltHeight) {
|
||||||
const navBarHeight = 36;
|
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';
|
import convertKeysToCamelCase from '../../../utils/convertKeysToCamelCase';
|
||||||
|
|
||||||
export default class ChartProps {
|
export default class ChartProps {
|
||||||
|
@ -29,3 +30,44 @@ export default class ChartProps {
|
||||||
this.setTooltip = setTooltip;
|
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 'nvd3/build/nv.d3.min.css';
|
||||||
|
|
||||||
import { t } from '../../locales';
|
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 { getScale, getColor } from '../../modules/colors/CategoricalColorNamespace';
|
||||||
import { formatDateVerbose } from '../../modules/dates';
|
import { formatDateVerbose } from '../../modules/dates';
|
||||||
import { d3TimeFormatPreset, d3FormatPreset } from '../../modules/utils';
|
import { d3TimeFormatPreset, d3FormatPreset } from '../../modules/utils';
|
||||||
|
@ -646,7 +646,7 @@ function nvd3Vis(element, props) {
|
||||||
// Time series annotations add additional data
|
// Time series annotations add additional data
|
||||||
const timeSeriesAnnotations = annotationLayers
|
const timeSeriesAnnotations = annotationLayers
|
||||||
.filter(layer => layer.show)
|
.filter(layer => layer.show)
|
||||||
.filter(layer => layer.annotationType === AnnotationTypes.TIME_SERIES)
|
.filter(layer => layer.annotationType === ANNOTATION_TYPES.TIME_SERIES)
|
||||||
.reduce((bushel, a) =>
|
.reduce((bushel, a) =>
|
||||||
bushel.concat((annotationData[a.name] || []).map((series) => {
|
bushel.concat((annotationData[a.name] || []).map((series) => {
|
||||||
if (!series) {
|
if (!series) {
|
||||||
|
@ -680,7 +680,7 @@ function nvd3Vis(element, props) {
|
||||||
if (isTimeSeries && annotationLayers.length > 0) {
|
if (isTimeSeries && annotationLayers.length > 0) {
|
||||||
// Formula annotations
|
// Formula annotations
|
||||||
const formulas = annotationLayers
|
const formulas = annotationLayers
|
||||||
.filter(a => a.annotationType === AnnotationTypes.FORMULA)
|
.filter(a => a.annotationType === ANNOTATION_TYPES.FORMULA)
|
||||||
.map(a => ({ ...a, formula: mathjs.parse(a.value) }));
|
.map(a => ({ ...a, formula: mathjs.parse(a.value) }));
|
||||||
|
|
||||||
let xMax;
|
let xMax;
|
||||||
|
@ -750,7 +750,7 @@ function nvd3Vis(element, props) {
|
||||||
if (annotationData) {
|
if (annotationData) {
|
||||||
// Event annotations
|
// Event annotations
|
||||||
annotationLayers.filter(x => (
|
annotationLayers.filter(x => (
|
||||||
x.annotationType === AnnotationTypes.EVENT &&
|
x.annotationType === ANNOTATION_TYPES.EVENT &&
|
||||||
annotationData && annotationData[x.name]
|
annotationData && annotationData[x.name]
|
||||||
)).forEach((config, index) => {
|
)).forEach((config, index) => {
|
||||||
const e = applyNativeColumns(config);
|
const e = applyNativeColumns(config);
|
||||||
|
@ -809,7 +809,7 @@ function nvd3Vis(element, props) {
|
||||||
|
|
||||||
// Interval annotations
|
// Interval annotations
|
||||||
annotationLayers.filter(x => (
|
annotationLayers.filter(x => (
|
||||||
x.annotationType === AnnotationTypes.INTERVAL &&
|
x.annotationType === ANNOTATION_TYPES.INTERVAL &&
|
||||||
annotationData && annotationData[x.name]
|
annotationData && annotationData[x.name]
|
||||||
)).forEach((config, index) => {
|
)).forEach((config, index) => {
|
||||||
const e = applyNativeColumns(config);
|
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