commit
b885f5725f
|
@ -1,4 +1,7 @@
|
|||
import timeSeriesToDygraph from 'src/utils/timeSeriesToDygraph';
|
||||
import {STROKE_WIDTH} from 'src/shared/constants';
|
||||
|
||||
const {light: strokeWidth} = STROKE_WIDTH;
|
||||
|
||||
describe('timeSeriesToDygraph', () => {
|
||||
it('parses a raw InfluxDB response into a dygraph friendly data format', () => {
|
||||
|
@ -46,9 +49,11 @@ describe('timeSeriesToDygraph', () => {
|
|||
dygraphSeries: {
|
||||
'm1.f1': {
|
||||
axis: 'y',
|
||||
strokeWidth,
|
||||
},
|
||||
'm1.f2': {
|
||||
axis: 'y',
|
||||
strokeWidth,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -156,12 +161,15 @@ describe('timeSeriesToDygraph', () => {
|
|||
dygraphSeries: {
|
||||
'm1.f1': {
|
||||
axis: 'y',
|
||||
strokeWidth,
|
||||
},
|
||||
'm1.f2': {
|
||||
axis: 'y',
|
||||
strokeWidth,
|
||||
},
|
||||
'm3.f3': {
|
||||
axis: 'y2',
|
||||
strokeWidth,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -5,40 +5,38 @@ import QueryTabItem from './QueryTabItem';
|
|||
import RenamePanelModal from './RenamePanelModal';
|
||||
import SimpleDropdown from 'src/shared/components/SimpleDropdown';
|
||||
|
||||
const {shape, func, bool, arrayOf} = PropTypes;
|
||||
const Panel = React.createClass({
|
||||
propTypes: {
|
||||
panel: shape({}).isRequired,
|
||||
queries: arrayOf(shape({})).isRequired,
|
||||
panel: PropTypes.shape({
|
||||
id: PropTypes.string.isRequired,
|
||||
}).isRequired,
|
||||
queries: PropTypes.arrayOf(PropTypes.shape({})).isRequired,
|
||||
timeRange: PropTypes.shape({
|
||||
upper: PropTypes.string,
|
||||
lower: PropTypes.string,
|
||||
}).isRequired,
|
||||
isExpanded: bool.isRequired,
|
||||
onTogglePanel: func.isRequired,
|
||||
actions: shape({
|
||||
chooseNamespace: func.isRequired,
|
||||
chooseMeasurement: func.isRequired,
|
||||
chooseTag: func.isRequired,
|
||||
groupByTag: func.isRequired,
|
||||
addQuery: func.isRequired,
|
||||
deleteQuery: func.isRequired,
|
||||
toggleField: func.isRequired,
|
||||
groupByTime: func.isRequired,
|
||||
toggleTagAcceptance: func.isRequired,
|
||||
applyFuncsToField: func.isRequired,
|
||||
deletePanel: func.isRequired,
|
||||
isExpanded: PropTypes.bool.isRequired,
|
||||
onTogglePanel: PropTypes.func.isRequired,
|
||||
actions: PropTypes.shape({
|
||||
chooseNamespace: PropTypes.func.isRequired,
|
||||
chooseMeasurement: PropTypes.func.isRequired,
|
||||
chooseTag: PropTypes.func.isRequired,
|
||||
groupByTag: PropTypes.func.isRequired,
|
||||
addQuery: PropTypes.func.isRequired,
|
||||
deleteQuery: PropTypes.func.isRequired,
|
||||
toggleField: PropTypes.func.isRequired,
|
||||
groupByTime: PropTypes.func.isRequired,
|
||||
toggleTagAcceptance: PropTypes.func.isRequired,
|
||||
applyFuncsToField: PropTypes.func.isRequired,
|
||||
deletePanel: PropTypes.func.isRequired,
|
||||
renamePanel: PropTypes.func.isRequired,
|
||||
}).isRequired,
|
||||
},
|
||||
|
||||
getInitialState() {
|
||||
return {
|
||||
activeQueryId: null,
|
||||
};
|
||||
setActiveQuery: PropTypes.func.isRequired,
|
||||
activeQueryID: PropTypes.string,
|
||||
},
|
||||
|
||||
handleSetActiveQuery(query) {
|
||||
this.setState({activeQueryId: query.id});
|
||||
this.props.setActiveQuery(query.id);
|
||||
},
|
||||
|
||||
handleAddQuery() {
|
||||
|
@ -63,8 +61,8 @@ const Panel = React.createClass({
|
|||
},
|
||||
|
||||
getActiveQuery() {
|
||||
const {queries} = this.props;
|
||||
const activeQuery = queries.find((query) => query.id === this.state.activeQueryId);
|
||||
const {queries, activeQueryID} = this.props;
|
||||
const activeQuery = queries.find((query) => query.id === activeQueryID);
|
||||
const defaultQuery = queries[0];
|
||||
|
||||
return activeQuery || defaultQuery;
|
||||
|
|
|
@ -24,7 +24,9 @@ const PanelBuilder = React.createClass({
|
|||
deletePanel: func.isRequired,
|
||||
}).isRequired,
|
||||
setActivePanel: func.isRequired,
|
||||
setActiveQuery: func.isRequired,
|
||||
activePanelID: string,
|
||||
activeQueryID: string,
|
||||
},
|
||||
|
||||
handleCreateExploer() {
|
||||
|
@ -32,7 +34,7 @@ const PanelBuilder = React.createClass({
|
|||
},
|
||||
|
||||
render() {
|
||||
const {activePanelID, width, actions, setActivePanel} = this.props;
|
||||
const {width, actions, setActivePanel, setActiveQuery, activePanelID, activeQueryID} = this.props;
|
||||
|
||||
return (
|
||||
<div className="panel-builder" style={{width}}>
|
||||
|
@ -40,7 +42,9 @@ const PanelBuilder = React.createClass({
|
|||
<PanelList
|
||||
actions={actions}
|
||||
setActivePanel={setActivePanel}
|
||||
setActiveQuery={setActiveQuery}
|
||||
activePanelID={activePanelID}
|
||||
activeQueryID={activeQueryID}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -15,7 +15,9 @@ const PanelList = React.createClass({
|
|||
queryConfigs: PropTypes.shape({}),
|
||||
actions: shape({}).isRequired,
|
||||
setActivePanel: func.isRequired,
|
||||
setActiveQuery: func.isRequired,
|
||||
activePanelID: string,
|
||||
activeQueryID: string,
|
||||
},
|
||||
|
||||
handleTogglePanel(panel) {
|
||||
|
@ -25,12 +27,12 @@ const PanelList = React.createClass({
|
|||
null : panel.id;
|
||||
|
||||
this.props.setActivePanel(activePanelID);
|
||||
// Reset the activeQueryID when toggling Exporations
|
||||
this.props.setActiveQuery(null);
|
||||
},
|
||||
|
||||
render() {
|
||||
const {actions, panels, timeRange, queryConfigs} = this.props;
|
||||
|
||||
const activePanelID = this.props.activePanelID;
|
||||
const {actions, panels, timeRange, queryConfigs, setActiveQuery, activeQueryID, activePanelID} = this.props;
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
@ -51,8 +53,10 @@ const PanelList = React.createClass({
|
|||
queries={queries}
|
||||
timeRange={timeRange}
|
||||
onTogglePanel={this.handleTogglePanel}
|
||||
setActiveQuery={setActiveQuery}
|
||||
isExpanded={panelID === activePanelID}
|
||||
actions={allActions}
|
||||
activeQueryID={activeQueryID}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
|
|
@ -6,22 +6,22 @@ import LineGraph from 'shared/components/LineGraph';
|
|||
import MultiTable from './MultiTable';
|
||||
const RefreshingLineGraph = AutoRefresh(LineGraph);
|
||||
|
||||
const {bool, shape, string, arrayOf} = PropTypes;
|
||||
const Visualization = React.createClass({
|
||||
propTypes: {
|
||||
timeRange: shape({
|
||||
upper: string,
|
||||
lower: string,
|
||||
timeRange: PropTypes.shape({
|
||||
upper: PropTypes.string,
|
||||
lower: PropTypes.string,
|
||||
}).isRequired,
|
||||
queryConfigs: arrayOf(shape({})).isRequired,
|
||||
isActive: bool.isRequired,
|
||||
name: string,
|
||||
queryConfigs: PropTypes.arrayOf(PropTypes.shape({})).isRequired,
|
||||
isActive: PropTypes.bool.isRequired,
|
||||
name: PropTypes.string,
|
||||
activeQueryIndex: PropTypes.number,
|
||||
},
|
||||
|
||||
contextTypes: {
|
||||
source: shape({
|
||||
links: shape({
|
||||
proxy: string.isRequired,
|
||||
source: PropTypes.shape({
|
||||
links: PropTypes.shape({
|
||||
proxy: PropTypes.string.isRequired,
|
||||
}).isRequired,
|
||||
}).isRequired,
|
||||
},
|
||||
|
@ -45,7 +45,7 @@ const Visualization = React.createClass({
|
|||
},
|
||||
|
||||
render() {
|
||||
const {queryConfigs, timeRange, isActive, name} = this.props;
|
||||
const {queryConfigs, timeRange, isActive, name, activeQueryIndex} = this.props;
|
||||
const {source} = this.context;
|
||||
const proxyLink = source.links.proxy;
|
||||
|
||||
|
@ -77,6 +77,7 @@ const Visualization = React.createClass({
|
|||
<RefreshingLineGraph
|
||||
queries={queries}
|
||||
autoRefresh={autoRefreshMs}
|
||||
activeQueryIndex={activeQueryIndex}
|
||||
/>
|
||||
) : <MultiTable queries={queries} />}
|
||||
</div>
|
||||
|
|
|
@ -14,6 +14,7 @@ const Visualizations = React.createClass({
|
|||
queryConfigs: shape({}).isRequired,
|
||||
width: string,
|
||||
activePanelID: string,
|
||||
activeQueryID: string,
|
||||
},
|
||||
|
||||
render() {
|
||||
|
@ -22,7 +23,9 @@ const Visualizations = React.createClass({
|
|||
const visualizations = Object.keys(panels).map((panelID) => {
|
||||
const panel = panels[panelID];
|
||||
const queries = panel.queryIds.map((id) => queryConfigs[id]);
|
||||
return <Visualization name={panel.name} key={panelID} queryConfigs={queries} timeRange={timeRange} isActive={panelID === activePanelID} />;
|
||||
const isActive = panelID === activePanelID;
|
||||
|
||||
return <Visualization activeQueryIndex={this.getActiveQueryIndex(panelID)} name={panel.name} key={panelID} queryConfigs={queries} timeRange={timeRange} isActive={isActive} />;
|
||||
});
|
||||
|
||||
return (
|
||||
|
@ -31,6 +34,21 @@ const Visualizations = React.createClass({
|
|||
</div>
|
||||
);
|
||||
},
|
||||
|
||||
getActiveQueryIndex(panelID) {
|
||||
const {activeQueryID, activePanelID, panels} = this.props;
|
||||
const isPanelActive = panelID === activePanelID;
|
||||
|
||||
if (!isPanelActive) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (activeQueryID === null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return panels[panelID].queryIds.indexOf(activeQueryID);
|
||||
},
|
||||
});
|
||||
|
||||
function mapStateToProps(state) {
|
||||
|
|
|
@ -49,14 +49,17 @@ const DataExplorer = React.createClass({
|
|||
|
||||
getInitialState() {
|
||||
return {
|
||||
activePanelId: null,
|
||||
activePanelID: null,
|
||||
activeQueryID: null,
|
||||
};
|
||||
},
|
||||
|
||||
handleSetActivePanel(id) {
|
||||
this.setState({
|
||||
activePanelID: id,
|
||||
});
|
||||
this.setState({activePanelID: id});
|
||||
},
|
||||
|
||||
handleSetActiveQuery(id) {
|
||||
this.setState({activeQueryID: id});
|
||||
},
|
||||
|
||||
render() {
|
||||
|
@ -76,8 +79,18 @@ const DataExplorer = React.createClass({
|
|||
explorerID={explorerID}
|
||||
/>
|
||||
<ResizeContainer>
|
||||
<PanelBuilder timeRange={timeRange} activePanelID={this.state.activePanelID} setActivePanel={this.handleSetActivePanel} />
|
||||
<Visualizations timeRange={timeRange} activePanelID={this.state.activePanelID} />
|
||||
<PanelBuilder
|
||||
timeRange={timeRange}
|
||||
activePanelID={this.state.activePanelID}
|
||||
activeQueryID={this.state.activeQueryID}
|
||||
setActiveQuery={this.handleSetActiveQuery}
|
||||
setActivePanel={this.handleSetActivePanel}
|
||||
/>
|
||||
<Visualizations
|
||||
timeRange={timeRange}
|
||||
activePanelID={this.state.activePanelID}
|
||||
activeQueryID={this.state.activeQueryID}
|
||||
/>
|
||||
</ResizeContainer>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -77,7 +77,6 @@ export default React.createClass({
|
|||
fillGraph: this.props.isGraphFilled,
|
||||
axisLineWidth: 2,
|
||||
gridLineWidth: 1,
|
||||
strokeWidth: 1.5,
|
||||
highlightCircleSize: 3,
|
||||
colors: finalLineColors,
|
||||
series: dygraphSeries,
|
||||
|
@ -143,7 +142,7 @@ export default React.createClass({
|
|||
}
|
||||
|
||||
const timeSeries = this.getTimeSeries();
|
||||
const {labels, ranges} = this.props;
|
||||
const {labels, ranges, options, dygraphSeries} = this.props;
|
||||
|
||||
dygraph.updateOptions({
|
||||
labels,
|
||||
|
@ -156,7 +155,8 @@ export default React.createClass({
|
|||
valueRange: getRange(timeSeries, ranges.y2),
|
||||
},
|
||||
},
|
||||
underlayCallback: this.props.options.underlayCallback,
|
||||
underlayCallback: options.underlayCallback,
|
||||
series: dygraphSeries,
|
||||
});
|
||||
|
||||
dygraph.resize();
|
||||
|
|
|
@ -7,20 +7,19 @@ import _ from 'lodash';
|
|||
import timeSeriesToDygraph from 'utils/timeSeriesToDygraph';
|
||||
import lastValues from 'src/shared/parsing/lastValues';
|
||||
|
||||
const {array, string, arrayOf, bool, shape} = PropTypes;
|
||||
|
||||
export default React.createClass({
|
||||
displayName: 'LineGraph',
|
||||
propTypes: {
|
||||
data: arrayOf(shape({}).isRequired).isRequired,
|
||||
title: string,
|
||||
data: PropTypes.arrayOf(PropTypes.shape({}).isRequired).isRequired,
|
||||
title: PropTypes.string,
|
||||
isFetchingInitially: PropTypes.bool,
|
||||
isRefreshing: PropTypes.bool,
|
||||
underlayCallback: PropTypes.func,
|
||||
isGraphFilled: bool,
|
||||
overrideLineColors: array,
|
||||
queries: arrayOf(shape({}).isRequired).isRequired,
|
||||
showSingleStat: bool,
|
||||
isGraphFilled: PropTypes.bool,
|
||||
overrideLineColors: PropTypes.array,
|
||||
queries: PropTypes.arrayOf(PropTypes.shape({}).isRequired).isRequired,
|
||||
showSingleStat: PropTypes.bool,
|
||||
activeQueryIndex: PropTypes.number,
|
||||
},
|
||||
|
||||
getDefaultProps() {
|
||||
|
@ -36,12 +35,13 @@ export default React.createClass({
|
|||
},
|
||||
|
||||
componentWillMount() {
|
||||
this._timeSeries = timeSeriesToDygraph(this.props.data);
|
||||
this._timeSeries = timeSeriesToDygraph(this.props.data, this.props.activeQueryIndex);
|
||||
},
|
||||
|
||||
componentWillUpdate(nextProps) {
|
||||
if (this.props.data !== nextProps.data) {
|
||||
this._timeSeries = timeSeriesToDygraph(nextProps.data);
|
||||
const {data, activeQueryIndex} = this.props;
|
||||
if (data !== nextProps.data || activeQueryIndex !== nextProps.activeQueryIndex) {
|
||||
this._timeSeries = timeSeriesToDygraph(nextProps.data, nextProps.activeQueryIndex);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -462,3 +462,8 @@ export const DEFAULT_LINE_COLORS = [
|
|||
],
|
||||
],
|
||||
];
|
||||
|
||||
export const STROKE_WIDTH = {
|
||||
heavy: 3.5,
|
||||
light: 1.5,
|
||||
};
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
import {STROKE_WIDTH} from 'src/shared/constants';
|
||||
/**
|
||||
* Accepts an array of raw influxdb responses and returns a format
|
||||
* that Dygraph understands.
|
||||
*/
|
||||
|
||||
export default function timeSeriesToDygraph(raw = []) {
|
||||
// activeQueryIndex is an optional argument that indicated which query's series
|
||||
// we want highlighted.
|
||||
export default function timeSeriesToDygraph(raw = [], activeQueryIndex) {
|
||||
const labels = ['time']; // all of the effective field names (i.e. <measurement>.<field>)
|
||||
const fieldToIndex = {}; // see parseSeries
|
||||
const dates = {}; // map of date as string to date value to minimize string coercion
|
||||
|
@ -90,7 +93,13 @@ export default function timeSeriesToDygraph(raw = []) {
|
|||
// ex given this timeSeries [Date, 10, 20, 30] field index at 2 would correspond to value 20
|
||||
fieldToIndex[effectiveFieldName] = labels.length;
|
||||
labels.push(effectiveFieldName);
|
||||
dygraphSeries[effectiveFieldName] = {axis: queryIndex === 0 ? 'y' : 'y2'};
|
||||
|
||||
const {light, heavy} = STROKE_WIDTH;
|
||||
|
||||
dygraphSeries[effectiveFieldName] = {
|
||||
axis: queryIndex === 0 ? 'y' : 'y2',
|
||||
strokeWidth: queryIndex === activeQueryIndex ? heavy : light,
|
||||
};
|
||||
});
|
||||
|
||||
(series.values || []).forEach(parseRow);
|
||||
|
|
Loading…
Reference in New Issue