diff --git a/ui/spec/utils/timeSeriesToDygraphSpec.js b/ui/spec/utils/timeSeriesToDygraphSpec.js
index a89bfd226..0b3b6c72a 100644
--- a/ui/spec/utils/timeSeriesToDygraphSpec.js
+++ b/ui/spec/utils/timeSeriesToDygraphSpec.js
@@ -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,
},
},
};
diff --git a/ui/src/chronograf/components/Panel.js b/ui/src/chronograf/components/Panel.js
index 94b83fb9c..c680e3e99 100644
--- a/ui/src/chronograf/components/Panel.js
+++ b/ui/src/chronograf/components/Panel.js
@@ -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;
diff --git a/ui/src/chronograf/components/PanelBuilder.js b/ui/src/chronograf/components/PanelBuilder.js
index b6b78e5d8..b827971f5 100644
--- a/ui/src/chronograf/components/PanelBuilder.js
+++ b/ui/src/chronograf/components/PanelBuilder.js
@@ -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 (
@@ -40,7 +42,9 @@ const PanelBuilder = React.createClass({
);
diff --git a/ui/src/chronograf/components/PanelList.js b/ui/src/chronograf/components/PanelList.js
index 0436b6477..411f31286 100644
--- a/ui/src/chronograf/components/PanelList.js
+++ b/ui/src/chronograf/components/PanelList.js
@@ -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 (
@@ -51,8 +53,10 @@ const PanelList = React.createClass({
queries={queries}
timeRange={timeRange}
onTogglePanel={this.handleTogglePanel}
+ setActiveQuery={setActiveQuery}
isExpanded={panelID === activePanelID}
actions={allActions}
+ activeQueryID={activeQueryID}
/>
);
})}
diff --git a/ui/src/chronograf/components/Visualization.js b/ui/src/chronograf/components/Visualization.js
index 2a58796a6..63a11c772 100644
--- a/ui/src/chronograf/components/Visualization.js
+++ b/ui/src/chronograf/components/Visualization.js
@@ -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({
) : }
diff --git a/ui/src/chronograf/components/Visualizations.js b/ui/src/chronograf/components/Visualizations.js
index 4b53cedd8..aa577b0d6 100644
--- a/ui/src/chronograf/components/Visualizations.js
+++ b/ui/src/chronograf/components/Visualizations.js
@@ -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 ;
+ const isActive = panelID === activePanelID;
+
+ return ;
});
return (
@@ -31,6 +34,21 @@ const Visualizations = React.createClass({
);
},
+
+ 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) {
diff --git a/ui/src/chronograf/containers/DataExplorer.js b/ui/src/chronograf/containers/DataExplorer.js
index 7303d86d5..f1fa92ee4 100644
--- a/ui/src/chronograf/containers/DataExplorer.js
+++ b/ui/src/chronograf/containers/DataExplorer.js
@@ -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}
/>
-
-
+
+
);
diff --git a/ui/src/shared/components/Dygraph.js b/ui/src/shared/components/Dygraph.js
index 74c6b3664..99b2a77ef 100644
--- a/ui/src/shared/components/Dygraph.js
+++ b/ui/src/shared/components/Dygraph.js
@@ -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();
diff --git a/ui/src/shared/components/LineGraph.js b/ui/src/shared/components/LineGraph.js
index a982fb64a..38e77e07c 100644
--- a/ui/src/shared/components/LineGraph.js
+++ b/ui/src/shared/components/LineGraph.js
@@ -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);
}
},
diff --git a/ui/src/shared/constants/index.js b/ui/src/shared/constants/index.js
index f0d3767af..aab4118e4 100644
--- a/ui/src/shared/constants/index.js
+++ b/ui/src/shared/constants/index.js
@@ -462,3 +462,8 @@ export const DEFAULT_LINE_COLORS = [
],
],
];
+
+export const STROKE_WIDTH = {
+ heavy: 3.5,
+ light: 1.5,
+};
diff --git a/ui/src/utils/timeSeriesToDygraph.js b/ui/src/utils/timeSeriesToDygraph.js
index 572b2b58e..9702842c4 100644
--- a/ui/src/utils/timeSeriesToDygraph.js
+++ b/ui/src/utils/timeSeriesToDygraph.js
@@ -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. .)
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);