From e5ef892c807e61d1363a3b73dfe4cc149e7f0c93 Mon Sep 17 00:00:00 2001 From: Hunter Trujillo Date: Mon, 3 Apr 2017 10:49:25 -0600 Subject: [PATCH 01/81] Small refactor for ES6ness. --- ui/src/kapacitor/containers/KapacitorPage.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ui/src/kapacitor/containers/KapacitorPage.js b/ui/src/kapacitor/containers/KapacitorPage.js index 87a3a52b4d..d7a1651ef8 100644 --- a/ui/src/kapacitor/containers/KapacitorPage.js +++ b/ui/src/kapacitor/containers/KapacitorPage.js @@ -70,11 +70,10 @@ export const KapacitorPage = React.createClass({ }, handleInputChange(e) { - const val = e.target.value - const name = e.target.name + const {value, name} = e.target this.setState((prevState) => { - const update = {[name]: val.trim()} + const update = {[name]: value.trim()} return {kapacitor: {...prevState.kapacitor, ...update}} }) }, From 7709d1833810c6d52f8f725918fd418c1b3a61d2 Mon Sep 17 00:00:00 2001 From: Hunter Trujillo Date: Mon, 3 Apr 2017 10:49:41 -0600 Subject: [PATCH 02/81] Fix kapacitor form submit bug. --- ui/src/kapacitor/components/KapacitorForm.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/kapacitor/components/KapacitorForm.js b/ui/src/kapacitor/components/KapacitorForm.js index ee0d77a680..afa6bf331c 100644 --- a/ui/src/kapacitor/components/KapacitorForm.js +++ b/ui/src/kapacitor/components/KapacitorForm.js @@ -104,7 +104,7 @@ const KapacitorForm = React.createClass({
- +
From 335230eb8fdb7e8765ea1857d48758615946d62f Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Mon, 27 Mar 2017 14:14:59 -0700 Subject: [PATCH 03/81] Create vis header --- ui/src/data_explorer/components/VisHeader.js | 35 ++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 ui/src/data_explorer/components/VisHeader.js diff --git a/ui/src/data_explorer/components/VisHeader.js b/ui/src/data_explorer/components/VisHeader.js new file mode 100644 index 0000000000..ea56c453c0 --- /dev/null +++ b/ui/src/data_explorer/components/VisHeader.js @@ -0,0 +1,35 @@ +import React, {PropTypes} from 'react' +import classNames from 'classnames' + +const VisHeader = ({views, view, onToggleView, name}) => ( +
+
+
    + {views.map(v => ( +
  • onToggleView(v)} + className={classNames("toggle-btn ", {active: view === v})}> + {v} +
  • + ))} +
+
+
{name}
+
+) + +const { + arrayOf, + func, + string, +} = PropTypes + +VisHeader.propTypes = { + views: arrayOf(string).isRequired, + view: string.isRequired, + onToggleView: func.isRequired, + name: string.isRequired, +} + +export default VisHeader From b9c1634d0f6f0155eab706b35ce222bb35974089 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Mon, 27 Mar 2017 14:17:06 -0700 Subject: [PATCH 04/81] Add vis header and console tab --- .../data_explorer/components/Visualization.js | 87 +++++++++---------- 1 file changed, 41 insertions(+), 46 deletions(-) diff --git a/ui/src/data_explorer/components/Visualization.js b/ui/src/data_explorer/components/Visualization.js index da8e6e7ac9..14cea8331a 100644 --- a/ui/src/data_explorer/components/Visualization.js +++ b/ui/src/data_explorer/components/Visualization.js @@ -8,6 +8,7 @@ import MultiTable from './MultiTable' const RefreshingLineGraph = AutoRefresh(LineGraph) const RefreshingSingleStat = AutoRefresh(SingleStat) +const VIEWS = ['graph', 'table', 'console'] const { arrayOf, @@ -41,12 +42,49 @@ const Visualization = React.createClass({ getInitialState() { return { - isGraphInView: true, + view: 'graph', } }, - handleToggleView() { - this.setState({isGraphInView: !this.state.isGraphInView}) + handleToggleView(view) { + this.setState({view}) + }, + + render() { + const {queryConfigs, timeRange, height, heightPixels} = this.props; + const {source} = this.context; + const proxyLink = source.links.proxy; + const {view} = this.state; + + const statements = queryConfigs.map((query) => { + const text = query.rawText || buildInfluxQLQuery(timeRange, query); + return {text, id: query.id}; + }); + const queries = statements.filter((s) => s.text !== null).map((s) => { + return {host: [proxyLink], text: s.text, id: s.id}; + }); + + return ( +
+ +
+ {this.renderVisualization(view, queries, heightPixels)} +
+
+ ); + }, + + renderVisualization(view, queries, heightPixels) { + switch (view) { + case 'graph': + return this.renderGraph(queries) + case 'table': + return + case 'console': + return
I'm a console
+ default: + this.renderGraph(queries) + } }, renderGraph(queries) { @@ -72,49 +110,6 @@ const Visualization = React.createClass({ /> ) }, - - render() { - const { - queryConfigs, - timeRange, - height, - heightPixels, - cellName, - } = this.props - - const {source} = this.context - const proxyLink = source.links.proxy - - const {isGraphInView} = this.state - const statements = queryConfigs.map((query) => { - const text = query.rawText || buildInfluxQLQuery(timeRange, query) - return {text, id: query.id} - }) - const queries = statements.filter((s) => s.text !== null).map((s) => { - return {host: [proxyLink], text: s.text, id: s.id} - }) - - return ( -
-
-
- {cellName || "Graph"} -
-
-
    -
  • Graph
  • -
  • Table
  • -
-
-
-
- {isGraphInView ? - this.renderGraph(queries) : - } -
-
- ) - }, }) export default Visualization From 79805bf8ecc6be324a0617e614157ca556535aea Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Wed, 29 Mar 2017 12:07:25 -0700 Subject: [PATCH 05/81] Remove console option --- ui/src/data_explorer/components/Visualization.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ui/src/data_explorer/components/Visualization.js b/ui/src/data_explorer/components/Visualization.js index 14cea8331a..79a8ea0486 100644 --- a/ui/src/data_explorer/components/Visualization.js +++ b/ui/src/data_explorer/components/Visualization.js @@ -8,7 +8,7 @@ import MultiTable from './MultiTable' const RefreshingLineGraph = AutoRefresh(LineGraph) const RefreshingSingleStat = AutoRefresh(SingleStat) -const VIEWS = ['graph', 'table', 'console'] +const VIEWS = ['graph', 'table'] const { arrayOf, @@ -80,8 +80,6 @@ const Visualization = React.createClass({ return this.renderGraph(queries) case 'table': return - case 'console': - return
I'm a console
default: this.renderGraph(queries) } From 0bf35d0069467c0c0158b4a3c2693b47499b8fb2 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Wed, 29 Mar 2017 12:18:10 -0700 Subject: [PATCH 06/81] Keep focus on enter while preventing new line --- ui/src/data_explorer/components/RawQueryEditor.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ui/src/data_explorer/components/RawQueryEditor.js b/ui/src/data_explorer/components/RawQueryEditor.js index 0f6873c31a..9763d481c3 100644 --- a/ui/src/data_explorer/components/RawQueryEditor.js +++ b/ui/src/data_explorer/components/RawQueryEditor.js @@ -24,9 +24,10 @@ const RawQueryEditor = React.createClass({ }, handleKeyDown(e) { + e.preventDefault() + if (e.keyCode === ENTER) { - this.handleUpdate() - this.editor.blur() + this.handleUpdate(); } else if (e.keyCode === ESCAPE) { this.setState({value: this.props.query.rawText}, () => { this.editor.blur() From 3ca248ea419cccc611c89b3cd00f36e04c380d06 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Wed, 29 Mar 2017 12:18:19 -0700 Subject: [PATCH 07/81] Fix spacing --- ui/src/data_explorer/components/QueryEditor.js | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/src/data_explorer/components/QueryEditor.js b/ui/src/data_explorer/components/QueryEditor.js index 52df49ada8..95b82063d3 100644 --- a/ui/src/data_explorer/components/QueryEditor.js +++ b/ui/src/data_explorer/components/QueryEditor.js @@ -12,6 +12,7 @@ const { shape, func, } = PropTypes + const QueryEditor = React.createClass({ propTypes: { query: shape({ From 2b6ffb09bf646790661a0ad25483090b3d52a5a0 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Wed, 29 Mar 2017 12:19:56 -0700 Subject: [PATCH 08/81] Include missing prop --- ui/src/data_explorer/components/QueryEditor.js | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/src/data_explorer/components/QueryEditor.js b/ui/src/data_explorer/components/QueryEditor.js index 95b82063d3..ca856759a2 100644 --- a/ui/src/data_explorer/components/QueryEditor.js +++ b/ui/src/data_explorer/components/QueryEditor.js @@ -31,6 +31,7 @@ const QueryEditor = React.createClass({ toggleField: func.isRequired, groupByTime: func.isRequired, toggleTagAcceptance: func.isRequired, + editRawText: func.isRequired, }).isRequired, }, From a1d7933c578882aa73b5855961ff8622119c4141 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Wed, 29 Mar 2017 12:23:04 -0700 Subject: [PATCH 09/81] Allow user to type :) --- ui/src/data_explorer/components/RawQueryEditor.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ui/src/data_explorer/components/RawQueryEditor.js b/ui/src/data_explorer/components/RawQueryEditor.js index 9763d481c3..e94b536ec1 100644 --- a/ui/src/data_explorer/components/RawQueryEditor.js +++ b/ui/src/data_explorer/components/RawQueryEditor.js @@ -24,9 +24,8 @@ const RawQueryEditor = React.createClass({ }, handleKeyDown(e) { - e.preventDefault() - if (e.keyCode === ENTER) { + e.preventDefault() this.handleUpdate(); } else if (e.keyCode === ESCAPE) { this.setState({value: this.props.query.rawText}, () => { From cd1271fde4aaba7b7abd869ec67113120640ff21 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Wed, 29 Mar 2017 13:38:29 -0700 Subject: [PATCH 10/81] Bubble AJAX errors up to fetchTimeSeries --- ui/src/data_explorer/components/Table.js | 20 +++++++++++--------- ui/src/shared/apis/timeSeries.js | 11 +++++++++-- ui/src/utils/queryUrlGenerator.js | 3 ++- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/ui/src/data_explorer/components/Table.js b/ui/src/data_explorer/components/Table.js index 1b515e7007..83f259c42d 100644 --- a/ui/src/data_explorer/components/Table.js +++ b/ui/src/data_explorer/components/Table.js @@ -52,20 +52,22 @@ const ChronoTable = React.createClass({ } }, - fetchCellData(query) { - this.setState({isLoading: true}) + async fetchCellData(query) { + this.setState({isLoading: true}); // second param is db, we want to leave this blank - fetchTimeSeries(query.host, undefined, query.text).then((resp) => { - const cellData = _.get(resp.data, ['results', '0', 'series', '0'], false) + try { + const {data} = await fetchTimeSeries(query.host, undefined, query.text) + const cellData = _.get(data, ['results', '0', 'series', '0'], false); + if (!cellData) { return this.setState({isLoading: false}) } - this.setState({ - cellData, - isLoading: false, - }) - }) + this.setState({cellData, isLoading: false}) + } catch (error) { + console.error(error.message) + this.setState({error: error.message, isLoading: false}) + } }, componentDidMount() { diff --git a/ui/src/shared/apis/timeSeries.js b/ui/src/shared/apis/timeSeries.js index 137e0c6cdf..1b81607a51 100644 --- a/ui/src/shared/apis/timeSeries.js +++ b/ui/src/shared/apis/timeSeries.js @@ -1,5 +1,12 @@ import {proxy} from 'utils/queryUrlGenerator' -export default function fetchTimeSeries(source, database, query) { - return proxy({source, query, database}) +const fetchTimeSeries = async (source, database, query) => { + try { + return await proxy({source, query, database}) + } catch (error) { + console.error('error from proxy: ', error) + throw error + } } + +export default fetchTimeSeries diff --git a/ui/src/utils/queryUrlGenerator.js b/ui/src/utils/queryUrlGenerator.js index a2bdc070d4..fba968da74 100644 --- a/ui/src/utils/queryUrlGenerator.js +++ b/ui/src/utils/queryUrlGenerator.js @@ -12,6 +12,7 @@ export const proxy = async ({source, query, db, rp}) => { }, }) } catch (error) { - console.error(error) // eslint-disable-line no-console + console.error(error) + throw error } } From cf8b8588b36b5a5422cb907d82d4bd854cea1ecb Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Wed, 29 Mar 2017 14:36:06 -0700 Subject: [PATCH 11/81] Introduce editRawQueryStatus action --- .../data_explorer/reducers/queryConfigSpec.js | 17 ++++++++++++++++- ui/src/data_explorer/actions/view/index.js | 10 ++++++++++ ui/src/data_explorer/reducers/queryConfigs.js | 9 +++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/ui/spec/data_explorer/reducers/queryConfigSpec.js b/ui/spec/data_explorer/reducers/queryConfigSpec.js index f23524e45b..95c352c966 100644 --- a/ui/spec/data_explorer/reducers/queryConfigSpec.js +++ b/ui/spec/data_explorer/reducers/queryConfigSpec.js @@ -10,7 +10,8 @@ import { groupByTime, toggleTagAcceptance, updateRawQuery, -} from 'src/data_explorer/actions/view' + editRawQueryStatus, +} from 'src/data_explorer/actions/view'; const fakeAddQueryAction = (panelID, queryID) => { return { @@ -321,4 +322,18 @@ describe('Chronograf.Reducers.queryConfig', () => { expect(nextState[queryId].rawText).to.equal('foo') }) + + it('updates a query\'s raw status', () => { + const queryId = 123 + const initialState = { + [queryId]: buildInitialState(queryId), + } + const status = 'your query was sweet' + const action = editRawQueryStatus(queryId, status) + + const nextState = reducer(initialState, action) + + expect(nextState[queryId].rawStatus).to.equal(status) + }) }) + diff --git a/ui/src/data_explorer/actions/view/index.js b/ui/src/data_explorer/actions/view/index.js index ddf4db6da0..fd24fe835b 100644 --- a/ui/src/data_explorer/actions/view/index.js +++ b/ui/src/data_explorer/actions/view/index.js @@ -128,3 +128,13 @@ export function updateRawQuery(queryID, text) { }, } } + +export function editRawQueryStatus(queryID, rawStatus) { + return { + type: 'EDIT_RAW_QUERY_STATUS', + payload: { + queryID, + rawStatus, + }, + }; +} diff --git a/ui/src/data_explorer/reducers/queryConfigs.js b/ui/src/data_explorer/reducers/queryConfigs.js index 61635ab05d..913ce72f47 100644 --- a/ui/src/data_explorer/reducers/queryConfigs.js +++ b/ui/src/data_explorer/reducers/queryConfigs.js @@ -145,6 +145,15 @@ export default function queryConfigs(state = {}, action) { [queryID]: nextQueryConfig, }) } + + case 'EDIT_RAW_QUERY_STATUS': { + const {queryID, rawStatus} = action.payload + const nextState = { + [queryID]: {...state[queryID], rawStatus}, + } + + return {...state, ...nextState} + } } return state } From 61a879c53b46b969f267a8a8d06b89282b430f16 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Wed, 29 Mar 2017 15:12:31 -0700 Subject: [PATCH 12/81] Display errors in admin console --- ui/src/data_explorer/components/MultiTable.js | 9 ++++--- .../components/RawQueryEditor.js | 4 ++- ui/src/data_explorer/components/Table.js | 26 ++++++++++++++----- .../data_explorer/components/Visualization.js | 10 ++++--- .../data_explorer/containers/DataExplorer.js | 1 + 5 files changed, 34 insertions(+), 16 deletions(-) diff --git a/ui/src/data_explorer/components/MultiTable.js b/ui/src/data_explorer/components/MultiTable.js index 7f69df2633..97899be841 100644 --- a/ui/src/data_explorer/components/MultiTable.js +++ b/ui/src/data_explorer/components/MultiTable.js @@ -18,6 +18,7 @@ const MultiTable = React.createClass({ text: string.isRequired, })), height: number, + onEditRawStatus: func.isRequired, }, getInitialState() { @@ -48,14 +49,14 @@ const MultiTable = React.createClass({ }, renderTable() { - const {height} = this.props - const query = this.getActiveQuery() - const noQuery = !query || !query.text + const {height, onEditRawStatus} = this.props; + const query = this.getActiveQuery(); + const noQuery = !query || !query.text; if (noQuery) { return null } - return + return
; }, renderTabs() { diff --git a/ui/src/data_explorer/components/RawQueryEditor.js b/ui/src/data_explorer/components/RawQueryEditor.js index e94b536ec1..68b95d0dda 100644 --- a/ui/src/data_explorer/components/RawQueryEditor.js +++ b/ui/src/data_explorer/components/RawQueryEditor.js @@ -26,7 +26,7 @@ const RawQueryEditor = React.createClass({ handleKeyDown(e) { if (e.keyCode === ENTER) { e.preventDefault() - this.handleUpdate(); + this.handleUpdate() } else if (e.keyCode === ESCAPE) { this.setState({value: this.props.query.rawText}, () => { this.editor.blur() @@ -45,6 +45,7 @@ const RawQueryEditor = React.createClass({ }, render() { + const {query: {rawStatus}} = this.props const {value} = this.state return ( @@ -58,6 +59,7 @@ const RawQueryEditor = React.createClass({ value={value} placeholder="Blank query" /> +
{rawStatus && rawStatus.error || rawStatus && rawStatus.success}
) }, diff --git a/ui/src/data_explorer/components/Table.js b/ui/src/data_explorer/components/Table.js index 83f259c42d..24263de63e 100644 --- a/ui/src/data_explorer/components/Table.js +++ b/ui/src/data_explorer/components/Table.js @@ -5,7 +5,7 @@ import fetchTimeSeries from 'shared/apis/timeSeries' import _ from 'lodash' import moment from 'moment' -const {oneOfType, number, string, shape, arrayOf} = PropTypes +const {oneOfType, number, string, shape, arrayOf, func} = PropTypes; const CustomCell = React.createClass({ propTypes: { @@ -34,6 +34,7 @@ const ChronoTable = React.createClass({ }), containerWidth: number.isRequired, height: number, + onEditRawStatus: func.isRequired, }, getInitialState() { @@ -54,19 +55,30 @@ const ChronoTable = React.createClass({ async fetchCellData(query) { this.setState({isLoading: true}); + const {onEditRawStatus} = this.props // second param is db, we want to leave this blank try { const {data} = await fetchTimeSeries(query.host, undefined, query.text) - const cellData = _.get(data, ['results', '0', 'series', '0'], false); + this.setState({isLoading: false}) - if (!cellData) { - return this.setState({isLoading: false}) + const error = _.get(data, ['results', '0', 'error'], false) + if (error) { + return onEditRawStatus(query.id, {error}) } - this.setState({cellData, isLoading: false}) + const cellData = _.get(data, ['results', '0', 'series', '0'], false); + onEditRawStatus(query.id, {success: 'Success!'}) + + if (!cellData) { + return + } + + this.setState({cellData}) } catch (error) { - console.error(error.message) - this.setState({error: error.message, isLoading: false}) + const {message} = error.data + this.setState({isLoading: false}) + console.error(message) + onEditRawStatus(query.id, {error: message}) } }, diff --git a/ui/src/data_explorer/components/Visualization.js b/ui/src/data_explorer/components/Visualization.js index 79a8ea0486..198aefd3b0 100644 --- a/ui/src/data_explorer/components/Visualization.js +++ b/ui/src/data_explorer/components/Visualization.js @@ -11,6 +11,7 @@ const RefreshingSingleStat = AutoRefresh(SingleStat) const VIEWS = ['graph', 'table'] const { + func, arrayOf, number, shape, @@ -30,6 +31,7 @@ const Visualization = React.createClass({ activeQueryIndex: number, height: string, heightPixels: number, + onEditRawStatus: func.isRequired, }, contextTypes: { @@ -51,7 +53,7 @@ const Visualization = React.createClass({ }, render() { - const {queryConfigs, timeRange, height, heightPixels} = this.props; + const {queryConfigs, timeRange, height, heightPixels, onEditRawStatus} = this.props; const {source} = this.context; const proxyLink = source.links.proxy; const {view} = this.state; @@ -68,18 +70,18 @@ const Visualization = React.createClass({
- {this.renderVisualization(view, queries, heightPixels)} + {this.renderVisualization(view, queries, heightPixels, onEditRawStatus)}
); }, - renderVisualization(view, queries, heightPixels) { + renderVisualization(view, queries, heightPixels, onEditRawStatus) { switch (view) { case 'graph': return this.renderGraph(queries) case 'table': - return + return default: this.renderGraph(queries) } diff --git a/ui/src/data_explorer/containers/DataExplorer.js b/ui/src/data_explorer/containers/DataExplorer.js index e6bfcd9288..7f494de541 100644 --- a/ui/src/data_explorer/containers/DataExplorer.js +++ b/ui/src/data_explorer/containers/DataExplorer.js @@ -88,6 +88,7 @@ const DataExplorer = React.createClass({ timeRange={timeRange} queryConfigs={queryConfigs} activeQueryIndex={0} + onEditRawStatus={queryConfigActions.editRawQueryStatus} /> Date: Thu, 30 Mar 2017 09:38:36 -0700 Subject: [PATCH 13/81] Fix linter errors --- ui/src/data_explorer/actions/view/index.js | 2 +- ui/src/data_explorer/components/MultiTable.js | 8 +++---- ui/src/data_explorer/components/Table.js | 6 +++--- .../data_explorer/components/Visualization.js | 21 ++++++++++--------- 4 files changed, 19 insertions(+), 18 deletions(-) diff --git a/ui/src/data_explorer/actions/view/index.js b/ui/src/data_explorer/actions/view/index.js index fd24fe835b..4ead10228f 100644 --- a/ui/src/data_explorer/actions/view/index.js +++ b/ui/src/data_explorer/actions/view/index.js @@ -136,5 +136,5 @@ export function editRawQueryStatus(queryID, rawStatus) { queryID, rawStatus, }, - }; + } } diff --git a/ui/src/data_explorer/components/MultiTable.js b/ui/src/data_explorer/components/MultiTable.js index 97899be841..a7c7765030 100644 --- a/ui/src/data_explorer/components/MultiTable.js +++ b/ui/src/data_explorer/components/MultiTable.js @@ -49,14 +49,14 @@ const MultiTable = React.createClass({ }, renderTable() { - const {height, onEditRawStatus} = this.props; - const query = this.getActiveQuery(); - const noQuery = !query || !query.text; + const {height, onEditRawStatus} = this.props + const query = this.getActiveQuery() + const noQuery = !query || !query.text if (noQuery) { return null } - return
; + return
}, renderTabs() { diff --git a/ui/src/data_explorer/components/Table.js b/ui/src/data_explorer/components/Table.js index 24263de63e..3257596ae0 100644 --- a/ui/src/data_explorer/components/Table.js +++ b/ui/src/data_explorer/components/Table.js @@ -5,7 +5,7 @@ import fetchTimeSeries from 'shared/apis/timeSeries' import _ from 'lodash' import moment from 'moment' -const {oneOfType, number, string, shape, arrayOf, func} = PropTypes; +const {oneOfType, number, string, shape, arrayOf, func} = PropTypes const CustomCell = React.createClass({ propTypes: { @@ -54,7 +54,7 @@ const ChronoTable = React.createClass({ }, async fetchCellData(query) { - this.setState({isLoading: true}); + this.setState({isLoading: true}) const {onEditRawStatus} = this.props // second param is db, we want to leave this blank try { @@ -66,7 +66,7 @@ const ChronoTable = React.createClass({ return onEditRawStatus(query.id, {error}) } - const cellData = _.get(data, ['results', '0', 'series', '0'], false); + const cellData = _.get(data, ['results', '0', 'series', '0'], false) onEditRawStatus(query.id, {success: 'Success!'}) if (!cellData) { diff --git a/ui/src/data_explorer/components/Visualization.js b/ui/src/data_explorer/components/Visualization.js index 198aefd3b0..72808a4e5a 100644 --- a/ui/src/data_explorer/components/Visualization.js +++ b/ui/src/data_explorer/components/Visualization.js @@ -5,6 +5,7 @@ import AutoRefresh from 'shared/components/AutoRefresh' import LineGraph from 'shared/components/LineGraph' import SingleStat from 'shared/components/SingleStat' import MultiTable from './MultiTable' +import VisHeader from 'src/data_explorer/components/VisHeader' const RefreshingLineGraph = AutoRefresh(LineGraph) const RefreshingSingleStat = AutoRefresh(SingleStat) @@ -53,18 +54,18 @@ const Visualization = React.createClass({ }, render() { - const {queryConfigs, timeRange, height, heightPixels, onEditRawStatus} = this.props; - const {source} = this.context; - const proxyLink = source.links.proxy; - const {view} = this.state; + const {queryConfigs, timeRange, height, heightPixels, onEditRawStatus} = this.props + const {source} = this.context + const proxyLink = source.links.proxy + const {view} = this.state const statements = queryConfigs.map((query) => { - const text = query.rawText || buildInfluxQLQuery(timeRange, query); - return {text, id: query.id}; - }); + const text = query.rawText || buildInfluxQLQuery(timeRange, query) + return {text, id: query.id} + }) const queries = statements.filter((s) => s.text !== null).map((s) => { - return {host: [proxyLink], text: s.text, id: s.id}; - }); + return {host: [proxyLink], text: s.text, id: s.id} + }) return (
@@ -73,7 +74,7 @@ const Visualization = React.createClass({ {this.renderVisualization(view, queries, heightPixels, onEditRawStatus)}
- ); + ) }, renderVisualization(view, queries, heightPixels, onEditRawStatus) { From ab0e386db09596b7e938f640e2f83617371a0154 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Thu, 30 Mar 2017 09:57:15 -0700 Subject: [PATCH 14/81] Improve builder semantics --- ui/src/data_explorer/components/QueryBuilder.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/ui/src/data_explorer/components/QueryBuilder.js b/ui/src/data_explorer/components/QueryBuilder.js index e829e593ad..d6f2fae9ba 100644 --- a/ui/src/data_explorer/components/QueryBuilder.js +++ b/ui/src/data_explorer/components/QueryBuilder.js @@ -108,10 +108,13 @@ const QueryBuilder = React.createClass({ {queries.map((q, i) => { let queryTabText if (q.rawText) { - queryTabText = 'InfluxQL' + queryTabText = 'Query Editor' + } else if (q.measurement && q.fields.length !== 0) { + queryTabText = `${q.measurement}.${q.fields[0].field}` } else { - queryTabText = (q.measurement && q.fields.length !== 0) ? `${q.measurement}.${q.fields[0].field}` : 'Query' + queryTabText = 'Query Builder' } + return ( Date: Thu, 30 Mar 2017 10:12:36 -0700 Subject: [PATCH 15/81] Pull list items onto variable --- ui/src/data_explorer/components/QueryBuilder.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui/src/data_explorer/components/QueryBuilder.js b/ui/src/data_explorer/components/QueryBuilder.js index d6f2fae9ba..f0d93466cd 100644 --- a/ui/src/data_explorer/components/QueryBuilder.js +++ b/ui/src/data_explorer/components/QueryBuilder.js @@ -144,8 +144,9 @@ const QueryBuilder = React.createClass({ }, renderAddQuery() { + const items = [{text: 'Query Builder'}, {text: 'Query Editor'}] return ( - + ) From 58a09756a173945931ae00f31e57951da722e9a4 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Thu, 30 Mar 2017 11:12:02 -0700 Subject: [PATCH 16/81] Add warn status type to rawStatus --- ui/src/data_explorer/components/RawQueryEditor.js | 2 +- ui/src/data_explorer/components/Table.js | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ui/src/data_explorer/components/RawQueryEditor.js b/ui/src/data_explorer/components/RawQueryEditor.js index 68b95d0dda..654063fbfd 100644 --- a/ui/src/data_explorer/components/RawQueryEditor.js +++ b/ui/src/data_explorer/components/RawQueryEditor.js @@ -59,7 +59,7 @@ const RawQueryEditor = React.createClass({ value={value} placeholder="Blank query" /> -
{rawStatus && rawStatus.error || rawStatus && rawStatus.success}
+
{rawStatus && rawStatus.error || rawStatus.warn || rawStatus.success}
) }, diff --git a/ui/src/data_explorer/components/Table.js b/ui/src/data_explorer/components/Table.js index 3257596ae0..708bfe54ab 100644 --- a/ui/src/data_explorer/components/Table.js +++ b/ui/src/data_explorer/components/Table.js @@ -61,9 +61,9 @@ const ChronoTable = React.createClass({ const {data} = await fetchTimeSeries(query.host, undefined, query.text) this.setState({isLoading: false}) - const error = _.get(data, ['results', '0', 'error'], false) - if (error) { - return onEditRawStatus(query.id, {error}) + const warn = _.get(data, ['results', '0', 'error'], false) + if (warn) { + return onEditRawStatus(query.id, {warn}) } const cellData = _.get(data, ['results', '0', 'series', '0'], false) From 5971429e5fb94e15a0c203a5ec20e12ab6fdc0e5 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Thu, 30 Mar 2017 11:12:02 -0700 Subject: [PATCH 17/81] Add warn status type to rawStatus --- ui/src/data_explorer/components/RawQueryEditor.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/ui/src/data_explorer/components/RawQueryEditor.js b/ui/src/data_explorer/components/RawQueryEditor.js index 654063fbfd..0cd6394ba8 100644 --- a/ui/src/data_explorer/components/RawQueryEditor.js +++ b/ui/src/data_explorer/components/RawQueryEditor.js @@ -59,10 +59,18 @@ const RawQueryEditor = React.createClass({ value={value} placeholder="Blank query" /> -
{rawStatus && rawStatus.error || rawStatus.warn || rawStatus.success}
+ {this.renderStatus(rawStatus)} ) }, + + renderStatus(rawStatus) { + if (!rawStatus) { + return null + } + + return
{rawStatus.error || rawStatus.warn || rawStatus.success}
+ }, }) export default RawQueryEditor From 0840fa3db6909cb08c9380d1e879ce1cf80ac910 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Thu, 30 Mar 2017 12:02:26 -0700 Subject: [PATCH 18/81] Show table when query editor is selected --- .../data_explorer/components/Visualization.js | 30 +++++++++++++++++-- .../data_explorer/containers/DataExplorer.js | 2 +- ui/src/utils/defaultQueryConfig.js | 1 + 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/ui/src/data_explorer/components/Visualization.js b/ui/src/data_explorer/components/Visualization.js index 72808a4e5a..75d78cd727 100644 --- a/ui/src/data_explorer/components/Visualization.js +++ b/ui/src/data_explorer/components/Visualization.js @@ -9,7 +9,10 @@ import VisHeader from 'src/data_explorer/components/VisHeader' const RefreshingLineGraph = AutoRefresh(LineGraph) const RefreshingSingleStat = AutoRefresh(SingleStat) -const VIEWS = ['graph', 'table'] + +const GRAPH = 'graph' +const TABLE = 'table' +const VIEWS = [GRAPH, TABLE] const { func, @@ -44,9 +47,30 @@ const Visualization = React.createClass({ }, getInitialState() { - return { - view: 'graph', + const {queryConfigs, activeQueryIndex} = this.props + if (!queryConfigs.length) { + return { + view: GRAPH, + } } + + return { + view: queryConfigs[activeQueryIndex].rawText ? TABLE : GRAPH, + } + }, + + componentWillReceiveProps(nextProps) { + const {queryConfigs, activeQueryIndex} = nextProps + if (!queryConfigs.length) { + return + } + + const activeQuery = queryConfigs[activeQueryIndex] + if (activeQuery && activeQuery.rawText) { + return this.setState({view: TABLE}) + } + + this.setState({view: GRAPH}) }, handleToggleView(view) { diff --git a/ui/src/data_explorer/containers/DataExplorer.js b/ui/src/data_explorer/containers/DataExplorer.js index 7f494de541..83dda828af 100644 --- a/ui/src/data_explorer/containers/DataExplorer.js +++ b/ui/src/data_explorer/containers/DataExplorer.js @@ -87,7 +87,7 @@ const DataExplorer = React.createClass({ autoRefresh={autoRefresh} timeRange={timeRange} queryConfigs={queryConfigs} - activeQueryIndex={0} + activeQueryIndex={activeQueryIndex} onEditRawStatus={queryConfigActions.editRawQueryStatus} /> diff --git a/ui/src/utils/defaultQueryConfig.js b/ui/src/utils/defaultQueryConfig.js index 97d579331b..de578708f3 100644 --- a/ui/src/utils/defaultQueryConfig.js +++ b/ui/src/utils/defaultQueryConfig.js @@ -12,5 +12,6 @@ export default function defaultQueryConfig(id) { }, areTagsAccepted: true, rawText: null, + rawStatus: null, } } From f6b0a13fbedea998dee4b1206e91a96ba8036f6f Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Thu, 30 Mar 2017 12:26:00 -0700 Subject: [PATCH 19/81] Not change view state if query is the same --- ui/src/data_explorer/components/Visualization.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/data_explorer/components/Visualization.js b/ui/src/data_explorer/components/Visualization.js index 75d78cd727..7a122ca4c9 100644 --- a/ui/src/data_explorer/components/Visualization.js +++ b/ui/src/data_explorer/components/Visualization.js @@ -61,7 +61,7 @@ const Visualization = React.createClass({ componentWillReceiveProps(nextProps) { const {queryConfigs, activeQueryIndex} = nextProps - if (!queryConfigs.length) { + if (!queryConfigs.length || activeQueryIndex === this.props.activeQueryIndex) { return } From 2a43dac1f36b12f5adaea0cdad349f3e860e0eee Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Thu, 30 Mar 2017 12:28:30 -0700 Subject: [PATCH 20/81] Update some primatives to use constants --- ui/src/data_explorer/components/Visualization.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/src/data_explorer/components/Visualization.js b/ui/src/data_explorer/components/Visualization.js index 7a122ca4c9..1d32b31270 100644 --- a/ui/src/data_explorer/components/Visualization.js +++ b/ui/src/data_explorer/components/Visualization.js @@ -94,7 +94,7 @@ const Visualization = React.createClass({ return (
-
+
{this.renderVisualization(view, queries, heightPixels, onEditRawStatus)}
@@ -103,9 +103,9 @@ const Visualization = React.createClass({ renderVisualization(view, queries, heightPixels, onEditRawStatus) { switch (view) { - case 'graph': + case GRAPH: return this.renderGraph(queries) - case 'table': + case TABLE: return default: this.renderGraph(queries) From 89285c2f410c2d05ccece82e9551ec4ff66e2793 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Thu, 30 Mar 2017 12:58:13 -0700 Subject: [PATCH 21/81] Change tabs in the table when builder changes tabs --- ui/src/data_explorer/components/MultiTable.js | 30 ++++++------------- .../data_explorer/components/Visualization.js | 17 +++++++---- .../data_explorer/containers/DataExplorer.js | 1 + 3 files changed, 21 insertions(+), 27 deletions(-) diff --git a/ui/src/data_explorer/components/MultiTable.js b/ui/src/data_explorer/components/MultiTable.js index a7c7765030..a129e9fc27 100644 --- a/ui/src/data_explorer/components/MultiTable.js +++ b/ui/src/data_explorer/components/MultiTable.js @@ -19,26 +19,18 @@ const MultiTable = React.createClass({ })), height: number, onEditRawStatus: func.isRequired, - }, - - getInitialState() { - return { - activeQueryId: null, - } + activeQueryIndex: number, + onSetActiveQueryIndex: func.isRequired, }, getActiveQuery() { - const {queries} = this.props - const activeQuery = queries.find((query) => query.id === this.state.activeQueryId) + const {queries, activeQueryIndex} = this.props + const activeQuery = queries[activeQueryIndex] const defaultQuery = queries[0] return activeQuery || defaultQuery }, - handleSetActiveTable(query) { - this.setState({activeQueryId: query.id}) - }, - render() { return (
@@ -60,16 +52,16 @@ const MultiTable = React.createClass({ }, renderTabs() { - const {queries} = this.props + const {queries, onSetActiveQueryIndex} = this.props return (
- {queries.map((q) => { + {queries.map((q, i) => { return ( onSetActiveQueryIndex(i)} /> ) })} @@ -89,14 +81,10 @@ const TabItem = React.createClass({ isActive: bool.isRequired, }, - handleSelect() { - this.props.onSelect(this.props.query) - }, - render() { - const {isActive} = this.props + const {isActive, onSelect} = this.props return ( -
+
{"Query"}
) diff --git a/ui/src/data_explorer/components/Visualization.js b/ui/src/data_explorer/components/Visualization.js index 1d32b31270..39bc74cb4c 100644 --- a/ui/src/data_explorer/components/Visualization.js +++ b/ui/src/data_explorer/components/Visualization.js @@ -36,6 +36,7 @@ const Visualization = React.createClass({ height: string, heightPixels: number, onEditRawStatus: func.isRequired, + onSetActiveQueryIndex: func.isRequired, }, contextTypes: { @@ -69,8 +70,6 @@ const Visualization = React.createClass({ if (activeQuery && activeQuery.rawText) { return this.setState({view: TABLE}) } - - this.setState({view: GRAPH}) }, handleToggleView(view) { @@ -78,7 +77,7 @@ const Visualization = React.createClass({ }, render() { - const {queryConfigs, timeRange, height, heightPixels, onEditRawStatus} = this.props + const {queryConfigs, timeRange, height, heightPixels, onEditRawStatus, activeQueryIndex, onSetActiveQueryIndex} = this.props const {source} = this.context const proxyLink = source.links.proxy const {view} = this.state @@ -95,18 +94,24 @@ const Visualization = React.createClass({
- {this.renderVisualization(view, queries, heightPixels, onEditRawStatus)} + {this.renderVisualization(view, queries, heightPixels, onEditRawStatus, activeQueryIndex, onSetActiveQueryIndex)}
) }, - renderVisualization(view, queries, heightPixels, onEditRawStatus) { + renderVisualization(view, queries, heightPixels, onEditRawStatus, activeQueryIndex, onSetActiveQueryIndex) { switch (view) { case GRAPH: return this.renderGraph(queries) case TABLE: - return + return () default: this.renderGraph(queries) } diff --git a/ui/src/data_explorer/containers/DataExplorer.js b/ui/src/data_explorer/containers/DataExplorer.js index 83dda828af..d2cff39072 100644 --- a/ui/src/data_explorer/containers/DataExplorer.js +++ b/ui/src/data_explorer/containers/DataExplorer.js @@ -89,6 +89,7 @@ const DataExplorer = React.createClass({ queryConfigs={queryConfigs} activeQueryIndex={activeQueryIndex} onEditRawStatus={queryConfigActions.editRawQueryStatus} + onSetActiveQueryIndex={this.handleSetActiveQueryIndex} /> Date: Thu, 30 Mar 2017 14:00:18 -0700 Subject: [PATCH 22/81] Remove MultiTable --- ui/src/data_explorer/components/MultiTable.js | 94 ------------------- .../data_explorer/components/QueryBuilder.js | 14 +-- ui/src/data_explorer/components/Table.js | 6 +- .../data_explorer/components/Visualization.js | 20 ++-- .../data_explorer/containers/DataExplorer.js | 1 - 5 files changed, 16 insertions(+), 119 deletions(-) delete mode 100644 ui/src/data_explorer/components/MultiTable.js diff --git a/ui/src/data_explorer/components/MultiTable.js b/ui/src/data_explorer/components/MultiTable.js deleted file mode 100644 index a129e9fc27..0000000000 --- a/ui/src/data_explorer/components/MultiTable.js +++ /dev/null @@ -1,94 +0,0 @@ -import React, {PropTypes} from 'react' -import Table from './Table' -import classNames from 'classnames' - -const { - arrayOf, - bool, - func, - number, - shape, - string, -} = PropTypes - -const MultiTable = React.createClass({ - propTypes: { - queries: arrayOf(shape({ - host: arrayOf(string.isRequired).isRequired, - text: string.isRequired, - })), - height: number, - onEditRawStatus: func.isRequired, - activeQueryIndex: number, - onSetActiveQueryIndex: func.isRequired, - }, - - getActiveQuery() { - const {queries, activeQueryIndex} = this.props - const activeQuery = queries[activeQueryIndex] - const defaultQuery = queries[0] - - return activeQuery || defaultQuery - }, - - render() { - return ( -
- {this.renderTabs()} - {this.renderTable()} -
- ) - }, - - renderTable() { - const {height, onEditRawStatus} = this.props - const query = this.getActiveQuery() - const noQuery = !query || !query.text - if (noQuery) { - return null - } - - return
- }, - - renderTabs() { - const {queries, onSetActiveQueryIndex} = this.props - return ( -
- {queries.map((q, i) => { - return ( - onSetActiveQueryIndex(i)} - /> - ) - })} -
- ) - }, -}) - -const TabItem = React.createClass({ - propTypes: { - query: shape({ - text: string.isRequired, - id: string.isRequired, - host: arrayOf(string.isRequired).isRequired, - }).isRequired, - onSelect: func.isRequired, - isActive: bool.isRequired, - }, - - render() { - const {isActive, onSelect} = this.props - return ( -
- {"Query"} -
- ) - }, -}) - -export default MultiTable diff --git a/ui/src/data_explorer/components/QueryBuilder.js b/ui/src/data_explorer/components/QueryBuilder.js index f0d93466cd..b1c1946c9b 100644 --- a/ui/src/data_explorer/components/QueryBuilder.js +++ b/ui/src/data_explorer/components/QueryBuilder.js @@ -3,6 +3,7 @@ import React, {PropTypes} from 'react' import QueryEditor from './QueryEditor' import QueryTabItem from './QueryTabItem' import SimpleDropdown from 'src/shared/components/SimpleDropdown' +import buildInfluxQLQuery from 'utils/influxql' const { arrayOf, @@ -98,7 +99,7 @@ const QueryBuilder = React.createClass({ }, renderQueryTabList() { - const {queries, activeQueryIndex, onDeleteQuery} = this.props + const {queries, activeQueryIndex, onDeleteQuery, timeRange} = this.props return (
@@ -106,15 +107,6 @@ const QueryBuilder = React.createClass({ {this.renderAddQuery()}
{queries.map((q, i) => { - let queryTabText - if (q.rawText) { - queryTabText = 'Query Editor' - } else if (q.measurement && q.fields.length !== 0) { - queryTabText = `${q.measurement}.${q.fields[0].field}` - } else { - queryTabText = 'Query Builder' - } - return ( ) })} diff --git a/ui/src/data_explorer/components/Table.js b/ui/src/data_explorer/components/Table.js index 708bfe54ab..9572957647 100644 --- a/ui/src/data_explorer/components/Table.js +++ b/ui/src/data_explorer/components/Table.js @@ -102,7 +102,7 @@ const ChronoTable = React.createClass({ // Table data as a list of array. render() { - const {containerWidth, height} = this.props + const {containerWidth, height, query} = this.props const {cellData, columnWidths, isLoading} = this.state const {columns, values} = cellData @@ -117,6 +117,10 @@ const ChronoTable = React.createClass({ const minWidth = 70 const styleAdjustedHeight = height - stylePixelOffset + if (!query) { + return
Please add a query below
+ } + if (!isLoading && !values.length) { return
Your query returned no data
} diff --git a/ui/src/data_explorer/components/Visualization.js b/ui/src/data_explorer/components/Visualization.js index 39bc74cb4c..9e1ec2fdbc 100644 --- a/ui/src/data_explorer/components/Visualization.js +++ b/ui/src/data_explorer/components/Visualization.js @@ -4,7 +4,7 @@ import classNames from 'classnames' import AutoRefresh from 'shared/components/AutoRefresh' import LineGraph from 'shared/components/LineGraph' import SingleStat from 'shared/components/SingleStat' -import MultiTable from './MultiTable' +import Table from './Table' import VisHeader from 'src/data_explorer/components/VisHeader' const RefreshingLineGraph = AutoRefresh(LineGraph) @@ -36,7 +36,6 @@ const Visualization = React.createClass({ height: string, heightPixels: number, onEditRawStatus: func.isRequired, - onSetActiveQueryIndex: func.isRequired, }, contextTypes: { @@ -77,7 +76,7 @@ const Visualization = React.createClass({ }, render() { - const {queryConfigs, timeRange, height, heightPixels, onEditRawStatus, activeQueryIndex, onSetActiveQueryIndex} = this.props + const {queryConfigs, timeRange, height, heightPixels, onEditRawStatus, activeQueryIndex} = this.props const {source} = this.context const proxyLink = source.links.proxy const {view} = this.state @@ -94,24 +93,21 @@ const Visualization = React.createClass({
- {this.renderVisualization(view, queries, heightPixels, onEditRawStatus, activeQueryIndex, onSetActiveQueryIndex)} + {this.renderVisualization(view, queries, heightPixels, onEditRawStatus, activeQueryIndex)}
) }, - renderVisualization(view, queries, heightPixels, onEditRawStatus, activeQueryIndex, onSetActiveQueryIndex) { + renderVisualization(view, queries, heightPixels, onEditRawStatus, activeQueryIndex) { + const activeQuery = queries[activeQueryIndex] + const defaultQuery = queries[0] + switch (view) { case GRAPH: return this.renderGraph(queries) case TABLE: - return () + return (
) default: this.renderGraph(queries) } diff --git a/ui/src/data_explorer/containers/DataExplorer.js b/ui/src/data_explorer/containers/DataExplorer.js index d2cff39072..83dda828af 100644 --- a/ui/src/data_explorer/containers/DataExplorer.js +++ b/ui/src/data_explorer/containers/DataExplorer.js @@ -89,7 +89,6 @@ const DataExplorer = React.createClass({ queryConfigs={queryConfigs} activeQueryIndex={activeQueryIndex} onEditRawStatus={queryConfigActions.editRawQueryStatus} - onSetActiveQueryIndex={this.handleSetActiveQueryIndex} /> Date: Thu, 30 Mar 2017 14:10:57 -0700 Subject: [PATCH 23/81] Update table CSS --- ui/src/style/pages/data-explorer/visualization.scss | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/ui/src/style/pages/data-explorer/visualization.scss b/ui/src/style/pages/data-explorer/visualization.scss index 232a4caf59..0492be779b 100644 --- a/ui/src/style/pages/data-explorer/visualization.scss +++ b/ui/src/style/pages/data-explorer/visualization.scss @@ -42,18 +42,6 @@ top: $de-vertical-margin; height: calc(100% - #{$de-graph-heading-height} - #{($de-vertical-margin * 2)}); - & > div { - position: absolute; - width: calc(100% - #{($de-vertical-margin * 2)}); - height: calc(100% - #{$de-vertical-margin}); - top: ($de-vertical-margin/2); - left: $de-vertical-margin;; - } - & > div .multi-table__tabs { - position: absolute; - height: 30px; - width: 100%; - } & > div > div:last-child { position: absolute; top: 30px; From 001421f198f16599cb4b53c5742d9cb0025b0d90 Mon Sep 17 00:00:00 2001 From: Alex P Date: Thu, 30 Mar 2017 16:46:39 -0700 Subject: [PATCH 24/81] Make query builder query tabs allow for more text before truncating --- ui/src/style/pages/data-explorer/query-builder.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/style/pages/data-explorer/query-builder.scss b/ui/src/style/pages/data-explorer/query-builder.scss index 79b9e32231..3cefa14989 100644 --- a/ui/src/style/pages/data-explorer/query-builder.scss +++ b/ui/src/style/pages/data-explorer/query-builder.scss @@ -133,7 +133,7 @@ font-weight: 600; white-space: nowrap; overflow: hidden; - max-width: 177px; + width: 90%; text-overflow: ellipsis; @include no-user-select(); } From 6e9191e8b259f154bd5f72d888c2d28eb5579fc2 Mon Sep 17 00:00:00 2001 From: Alex P Date: Thu, 30 Mar 2017 16:47:03 -0700 Subject: [PATCH 25/81] Make visualization toggle capitalized --- ui/src/style/pages/data-explorer/visualization.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/src/style/pages/data-explorer/visualization.scss b/ui/src/style/pages/data-explorer/visualization.scss index 0492be779b..65bc361adf 100644 --- a/ui/src/style/pages/data-explorer/visualization.scss +++ b/ui/src/style/pages/data-explorer/visualization.scss @@ -19,6 +19,7 @@ .toggle { margin: 0; + text-transform: capitalize; } } .graph-title { From 6aa2c9b903cefbae1f736c2da4e6b019f4ec6f6f Mon Sep 17 00:00:00 2001 From: Alex P Date: Thu, 30 Mar 2017 16:52:57 -0700 Subject: [PATCH 26/81] Make query tab delete buttons scale with everything else They looked mad tiny on big screens --- .../style/pages/data-explorer/font-scale.scss | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/ui/src/style/pages/data-explorer/font-scale.scss b/ui/src/style/pages/data-explorer/font-scale.scss index 5e7ce73a5c..f61f82af52 100644 --- a/ui/src/style/pages/data-explorer/font-scale.scss +++ b/ui/src/style/pages/data-explorer/font-scale.scss @@ -45,6 +45,15 @@ $breakpoint-c: 2100px; .btn-xs { font-size: 13.5px; } + .query-builder--tab-delete { + width: 20px; + height: 20px; + + &:before, + &:after { + width: 10px; + } + } } } @media only screen and (min-width: $breakpoint-c) { @@ -78,5 +87,14 @@ $breakpoint-c: 2100px; .multi-select-dropdown .dropdown-toggle { width: 140px; } + .query-builder--tab-delete { + width: 22px; + height: 22px; + + &:before, + &:after { + width: 12px; + } + } } } \ No newline at end of file From 7004df6f6382daf98445661c6217365158dc4e7b Mon Sep 17 00:00:00 2001 From: Alex P Date: Thu, 30 Mar 2017 18:51:33 -0700 Subject: [PATCH 27/81] Make builder queries vertically centered (again) --- ui/src/data_explorer/components/RawQueryEditor.js | 12 ++++++++++-- ui/src/style/pages/data-explorer/query-builder.scss | 3 ++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/ui/src/data_explorer/components/RawQueryEditor.js b/ui/src/data_explorer/components/RawQueryEditor.js index 0cd6394ba8..e135bb25dc 100644 --- a/ui/src/data_explorer/components/RawQueryEditor.js +++ b/ui/src/data_explorer/components/RawQueryEditor.js @@ -1,4 +1,5 @@ import React, {PropTypes} from 'react' +import classNames from 'classnames' const ENTER = 13 const ESCAPE = 27 @@ -57,7 +58,9 @@ const RawQueryEditor = React.createClass({ onBlur={this.handleUpdate} ref={(editor) => this.editor = editor} value={value} - placeholder="Blank query" + placeholder="Enter a query..." + autoComplete="off" + spellCheck="false" /> {this.renderStatus(rawStatus)} @@ -69,7 +72,12 @@ const RawQueryEditor = React.createClass({ return null } - return
{rawStatus.error || rawStatus.warn || rawStatus.success}
+ return ( +
+ + {rawStatus.error || rawStatus.warn || rawStatus.success} +
+ ) }, }) diff --git a/ui/src/style/pages/data-explorer/query-builder.scss b/ui/src/style/pages/data-explorer/query-builder.scss index 3cefa14989..98bdad1d63 100644 --- a/ui/src/style/pages/data-explorer/query-builder.scss +++ b/ui/src/style/pages/data-explorer/query-builder.scss @@ -165,7 +165,8 @@ $query-builder--column-heading-height: 50px; position: relative; pre { - display: block; + display: flex; + align-items: center; padding: 7px; border: 2px solid $query-editor-tab-inactive; background-color: $query-editor-tab-inactive; From 7a9858bdda23be33eac73b2a8b08ba51a5912982 Mon Sep 17 00:00:00 2001 From: Alex P Date: Thu, 30 Mar 2017 18:52:03 -0700 Subject: [PATCH 28/81] Disable text selection in query editor empty states --- ui/src/style/pages/data-explorer/query-editor.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ui/src/style/pages/data-explorer/query-editor.scss b/ui/src/style/pages/data-explorer/query-editor.scss index 39f5830761..695215891d 100644 --- a/ui/src/style/pages/data-explorer/query-editor.scss +++ b/ui/src/style/pages/data-explorer/query-editor.scss @@ -174,6 +174,10 @@ background-color: $g3-castle; border-radius: 0 $radius $radius 0; margin-top: 16px; + &, + & > * { + @include no-user-select(); + } } // Hidden dropdowns From 8bb7b4034b12b43e903db57e058e833ac4517e44 Mon Sep 17 00:00:00 2001 From: Alex P Date: Thu, 30 Mar 2017 18:52:51 -0700 Subject: [PATCH 29/81] Change editor queries to be blue --- ui/src/style/pages/data-explorer/raw-text.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/style/pages/data-explorer/raw-text.scss b/ui/src/style/pages/data-explorer/raw-text.scss index 5999f752ef..6e49f5fb46 100644 --- a/ui/src/style/pages/data-explorer/raw-text.scss +++ b/ui/src/style/pages/data-explorer/raw-text.scss @@ -25,7 +25,7 @@ text-shadow: none !important; } -$raw-text-color: $c-comet; +$raw-text-color: $c-pool; .raw-text--field { @include custom-scrollbar($g2-kevlar, $raw-text-color); From d1a6057c0b5790b90d6570e39c2df17436c8eef6 Mon Sep 17 00:00:00 2001 From: Alex P Date: Thu, 30 Mar 2017 18:53:30 -0700 Subject: [PATCH 30/81] Style raw text status bar is responsive --- .../style/pages/data-explorer/font-scale.scss | 39 +++++++++++-- .../style/pages/data-explorer/raw-text.scss | 56 +++++++++++++++++-- 2 files changed, 83 insertions(+), 12 deletions(-) diff --git a/ui/src/style/pages/data-explorer/font-scale.scss b/ui/src/style/pages/data-explorer/font-scale.scss index f61f82af52..9ebf9a387e 100644 --- a/ui/src/style/pages/data-explorer/font-scale.scss +++ b/ui/src/style/pages/data-explorer/font-scale.scss @@ -36,9 +36,6 @@ $breakpoint-c: 2100px; .query-builder--column-heading { font-size: 16px; } - .query-builder--query-preview pre code { - font-size: 13.5px; - } .toggle-sm .toggle-btn { font-size: 14px; } @@ -54,6 +51,33 @@ $breakpoint-c: 2100px; width: 10px; } } + .query-builder--tabs-heading { + height: 80px; + } + .query-builder--query-preview pre { + height: calc(80px - 4px); + } + .query-builder--columns { + top: 80px; + height: calc(100% - 80px); + } + .raw-text--field, + .query-builder--query-preview pre, + .query-builder--query-preview pre code { + font-size: 14px !important; + line-height: 16px !important; + } + .raw-text--field { + height: 48px; + padding: 12px 10px 0 10px; + } + .raw-text--status { + height: 28px; + padding: 0 10px; + } + .query-builder--query-preview pre { + padding: 10px; + } } } @media only screen and (min-width: $breakpoint-c) { @@ -75,9 +99,6 @@ $breakpoint-c: 2100px; font-weight: 400; text-transform: uppercase; } - .query-builder--query-preview pre code { - font-size: 14px; - } .toggle-sm .toggle-btn { font-size: 14px; } @@ -96,5 +117,11 @@ $breakpoint-c: 2100px; width: 12px; } } + .raw-text--field, + .query-builder--query-preview pre, + .query-builder--query-preview pre code { + font-size: 16px !important; + line-height: 18px !important; + } } } \ No newline at end of file diff --git a/ui/src/style/pages/data-explorer/raw-text.scss b/ui/src/style/pages/data-explorer/raw-text.scss index 6e49f5fb46..2899bc42e2 100644 --- a/ui/src/style/pages/data-explorer/raw-text.scss +++ b/ui/src/style/pages/data-explorer/raw-text.scss @@ -26,17 +26,19 @@ } $raw-text-color: $c-pool; +$raw-text-height: 38px; .raw-text--field { @include custom-scrollbar($g2-kevlar, $raw-text-color); display: block; width: 100%; - height: ($query-builder--preview-height - 4px); + height: $raw-text-height; background-color: $g2-kevlar; border: 2px solid $g2-kevlar; + border-bottom: 0; color: $raw-text-color; padding: 7px; - border-radius: $radius; + border-radius: $radius $radius 0 0; margin: 0; transition: color 0.25s ease, @@ -55,13 +57,55 @@ $raw-text-color: $c-pool; &:-moz-placeholder { /* Firefox 18- */ color: $g8-storm; } - &:hover { - background-color: $g3-castle; - border-color: $g3-castle; + &:hover, + &:hover + .raw-text--status { + border-color: $g5-pepper; } &:focus { outline: none; color: $raw-text-color !important; border-color: $c-pool; } -} \ No newline at end of file + &:focus + .raw-text--status { + border-color: $c-pool; + } +} +.raw-text--status { + width: 100%; + height: ($query-builder--preview-height - 2px - $raw-text-height); + line-height: 12px; + font-size: 12px; + background-color: $g2-kevlar; + border: 2px solid $g2-kevlar; + padding: 0 7px; + border-radius: 0 0 $radius $radius; + border-top: 0; + color: $g11-sidewalk; + font-family: $code-font; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + transition: + color 0.25s ease, + background-color 0.25s ease, + border-color 0.25s ease; + + span.icon { + margin-right: 5px; + } + + /* Error State */ + &.raw-text--error { + color: $c-dreamsicle; + } + + /* Warning State */ + &.raw-text--warning { + color: $c-comet; + } + + /* Success State */ + &.raw-text--success { + color: $c-rainforest; + } +} \ No newline at end of file From eee7b9a592aae8a1cb1f8d46e6b98f4ee05c24ed Mon Sep 17 00:00:00 2001 From: Alex P Date: Thu, 30 Mar 2017 19:20:38 -0700 Subject: [PATCH 31/81] Make DE table empty state more readable --- ui/src/style/pages/data-explorer/visualization.scss | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ui/src/style/pages/data-explorer/visualization.scss b/ui/src/style/pages/data-explorer/visualization.scss index 65bc361adf..25fbd3eb43 100644 --- a/ui/src/style/pages/data-explorer/visualization.scss +++ b/ui/src/style/pages/data-explorer/visualization.scss @@ -53,9 +53,11 @@ height: 100% !important; } .generic-empty-state { - background-color: $g6-smoke; + background-color: transparent; padding: 50px 0; height: 100%; + font-size: 22px; + @include no-user-select(); } } .graph-container { From 90a38c558ce9da919b943aba087b38de220aa5b1 Mon Sep 17 00:00:00 2001 From: Alex P Date: Thu, 30 Mar 2017 19:21:07 -0700 Subject: [PATCH 32/81] Remove vestiges of "active" graphs in DE styles --- ui/src/data_explorer/components/Visualization.js | 2 +- .../style/pages/data-explorer/visualization.scss | 16 ---------------- 2 files changed, 1 insertion(+), 17 deletions(-) diff --git a/ui/src/data_explorer/components/Visualization.js b/ui/src/data_explorer/components/Visualization.js index 9e1ec2fdbc..aaf48b9966 100644 --- a/ui/src/data_explorer/components/Visualization.js +++ b/ui/src/data_explorer/components/Visualization.js @@ -90,7 +90,7 @@ const Visualization = React.createClass({ }) return ( -
+
{this.renderVisualization(view, queries, heightPixels, onEditRawStatus, activeQueryIndex)} diff --git a/ui/src/style/pages/data-explorer/visualization.scss b/ui/src/style/pages/data-explorer/visualization.scss index 25fbd3eb43..3fdf520c49 100644 --- a/ui/src/style/pages/data-explorer/visualization.scss +++ b/ui/src/style/pages/data-explorer/visualization.scss @@ -91,22 +91,6 @@ } } -.data-explorer .graph-panel__refreshing { - top: -31px !important; -} - - -/* Active State */ -.graph.active { - .graph-heading, - .graph-container { - background-color: $graph-active-color; - } - .graph-title { - color: $g20-white; - } -} - .graph-empty { width: 100%; height: 300px; From 368bf7c78574ab5026b4c0f7494719380d211459 Mon Sep 17 00:00:00 2001 From: Alex P Date: Thu, 30 Mar 2017 19:21:28 -0700 Subject: [PATCH 33/81] Clean up styles --- ui/src/style/pages/data-explorer/visualization.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/style/pages/data-explorer/visualization.scss b/ui/src/style/pages/data-explorer/visualization.scss index 3fdf520c49..3a9ea19dae 100644 --- a/ui/src/style/pages/data-explorer/visualization.scss +++ b/ui/src/style/pages/data-explorer/visualization.scss @@ -35,7 +35,7 @@ display: flex; align-items: center; } -.table-container { +.graph .table-container { background-color: $graph-bg-color; border-radius: 0 0 $graph-radius $graph-radius; padding: 8px 16px; From 636a5c2b37e86fa4a7f40180631bc00818dc86d4 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Mon, 3 Apr 2017 10:15:08 -0700 Subject: [PATCH 34/81] Start query editor with placeholder text --- .../data_explorer/components/QueryBuilder.js | 4 ++-- .../data_explorer/components/QueryEditor.js | 4 ++-- .../components/RawQueryEditor.js | 2 +- ui/src/data_explorer/components/Table.js | 23 ++++++++++++++----- 4 files changed, 22 insertions(+), 11 deletions(-) diff --git a/ui/src/data_explorer/components/QueryBuilder.js b/ui/src/data_explorer/components/QueryBuilder.js index b1c1946c9b..b8adeb832b 100644 --- a/ui/src/data_explorer/components/QueryBuilder.js +++ b/ui/src/data_explorer/components/QueryBuilder.js @@ -52,7 +52,7 @@ const QueryBuilder = React.createClass({ handleAddRawQuery() { const newIndex = this.props.queries.length - this.props.actions.addQuery({rawText: `SELECT "fields" from "db"."rp"."measurement"`}) + this.props.actions.addQuery({rawText: ''}) this.handleSetActiveQueryIndex(newIndex) }, @@ -115,7 +115,7 @@ const QueryBuilder = React.createClass({ query={q} onSelect={this.handleSetActiveQueryIndex} onDelete={onDeleteQuery} - queryTabText={q.rawText || buildInfluxQLQuery(timeRange, q) || `SELECT "fields" FROM "db"."rp"."measurement"`} + queryTabText={q.rawText || buildInfluxQLQuery(timeRange, q) || `Query ${i + 1}`} /> ) })} diff --git a/ui/src/data_explorer/components/QueryEditor.js b/ui/src/data_explorer/components/QueryEditor.js index ca856759a2..afd4772169 100644 --- a/ui/src/data_explorer/components/QueryEditor.js +++ b/ui/src/data_explorer/components/QueryEditor.js @@ -91,9 +91,9 @@ const QueryEditor = React.createClass({ renderQuery() { const {query, timeRange} = this.props - const statement = query.rawText || buildInfluxQLQuery(timeRange, query) || `SELECT "fields" FROM "db"."rp"."measurement"` + const statement = query.rawText || buildInfluxQLQuery(timeRange, query) || `Select a database, measurement, and field below.` - if (!query.rawText) { + if (typeof query.rawText !== 'string') { return (
{statement}
diff --git a/ui/src/data_explorer/components/RawQueryEditor.js b/ui/src/data_explorer/components/RawQueryEditor.js index e135bb25dc..22a1fecd5f 100644 --- a/ui/src/data_explorer/components/RawQueryEditor.js +++ b/ui/src/data_explorer/components/RawQueryEditor.js @@ -29,7 +29,7 @@ const RawQueryEditor = React.createClass({ e.preventDefault() this.handleUpdate() } else if (e.keyCode === ESCAPE) { - this.setState({value: this.props.query.rawText}, () => { + this.setState({value: this.state.value}, () => { this.editor.blur() }) } diff --git a/ui/src/data_explorer/components/Table.js b/ui/src/data_explorer/components/Table.js index 9572957647..d36c79107e 100644 --- a/ui/src/data_explorer/components/Table.js +++ b/ui/src/data_explorer/components/Table.js @@ -6,6 +6,10 @@ import _ from 'lodash' import moment from 'moment' const {oneOfType, number, string, shape, arrayOf, func} = PropTypes +const emptyCells = { + columns: [], + values: [], +} const CustomCell = React.createClass({ propTypes: { @@ -39,10 +43,7 @@ const ChronoTable = React.createClass({ getInitialState() { return { - cellData: { - columns: [], - values: [], - }, + cellData: emptyCells, columnWidths: {}, } }, @@ -54,6 +55,10 @@ const ChronoTable = React.createClass({ }, async fetchCellData(query) { + if (!query.text) { + return + } + this.setState({isLoading: true}) const {onEditRawStatus} = this.props // second param is db, we want to leave this blank @@ -87,9 +92,15 @@ const ChronoTable = React.createClass({ }, componentWillReceiveProps(nextProps) { - if (this.props.query.text !== nextProps.query.text) { - this.fetchCellData(nextProps.query) + if (!this.props.query || !nextProps.query) { + return } + + if (this.props.query.text === nextProps.query.text) { + return + } + + this.fetchCellData(nextProps.query) }, handleColumnResize(newColumnWidth, columnKey) { From b7607d156c42a0aa068e0e675bda46e7b675b967 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Mon, 3 Apr 2017 10:15:23 -0700 Subject: [PATCH 35/81] Handle empty results --- ui/src/data_explorer/components/Table.js | 26 +++++++++++++++--------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/ui/src/data_explorer/components/Table.js b/ui/src/data_explorer/components/Table.js index d36c79107e..851c01b84b 100644 --- a/ui/src/data_explorer/components/Table.js +++ b/ui/src/data_explorer/components/Table.js @@ -66,21 +66,27 @@ const ChronoTable = React.createClass({ const {data} = await fetchTimeSeries(query.host, undefined, query.text) this.setState({isLoading: false}) - const warn = _.get(data, ['results', '0', 'error'], false) - if (warn) { - return onEditRawStatus(query.id, {warn}) - } - - const cellData = _.get(data, ['results', '0', 'series', '0'], false) - onEditRawStatus(query.id, {success: 'Success!'}) - - if (!cellData) { + const results = _.get(data, ['results', '0'], false) + if (!results) { return } + if (_.isEmpty(results)) { + this.setState({cellData: emptyCells}) + return onEditRawStatus(query.id, {warn: 'Your query is syntactically correct but returned no results'}) + } + + const warn = _.get(results, 'error', false) + if (warn) { + this.setState({cellData: emptyCells}) + return onEditRawStatus(query.id, {warn}) + } + + const cellData = _.get(results, ['series', '0'], {}) + onEditRawStatus(query.id, {success: 'Success!'}) this.setState({cellData}) } catch (error) { - const {message} = error.data + const message = _.get(error, ['data', 'message'], error) this.setState({isLoading: false}) console.error(message) onEditRawStatus(query.id, {error: message}) From 3da0df56e6196a073d1c2732d7db3fe2f51513ac Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Mon, 3 Apr 2017 11:28:01 -0700 Subject: [PATCH 36/81] Fix spacing on input with no status --- ui/src/data_explorer/components/RawQueryEditor.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ui/src/data_explorer/components/RawQueryEditor.js b/ui/src/data_explorer/components/RawQueryEditor.js index 22a1fecd5f..41e9398162 100644 --- a/ui/src/data_explorer/components/RawQueryEditor.js +++ b/ui/src/data_explorer/components/RawQueryEditor.js @@ -69,7 +69,9 @@ const RawQueryEditor = React.createClass({ renderStatus(rawStatus) { if (!rawStatus) { - return null + return ( +
+ ) } return ( From c81fa02692aa72e159bc5f1e8fe4e25636add4a5 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Mon, 3 Apr 2017 11:32:30 -0700 Subject: [PATCH 37/81] Handle empty rawText case --- ui/src/data_explorer/components/Visualization.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/data_explorer/components/Visualization.js b/ui/src/data_explorer/components/Visualization.js index aaf48b9966..4eb28b84ec 100644 --- a/ui/src/data_explorer/components/Visualization.js +++ b/ui/src/data_explorer/components/Visualization.js @@ -55,7 +55,7 @@ const Visualization = React.createClass({ } return { - view: queryConfigs[activeQueryIndex].rawText ? TABLE : GRAPH, + view: typeof queryConfigs[activeQueryIndex].rawText === 'string' ? TABLE : GRAPH, } }, From 8a03b22c90e00ab956ef887c063fb89e5176f5cc Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Mon, 3 Apr 2017 11:47:41 -0700 Subject: [PATCH 38/81] Change empty how we handle empty state --- ui/src/data_explorer/components/Table.js | 2 +- ui/src/data_explorer/components/Visualization.js | 6 +++--- ui/src/data_explorer/containers/DataExplorer.js | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ui/src/data_explorer/components/Table.js b/ui/src/data_explorer/components/Table.js index 851c01b84b..c8d33f0ed5 100644 --- a/ui/src/data_explorer/components/Table.js +++ b/ui/src/data_explorer/components/Table.js @@ -55,7 +55,7 @@ const ChronoTable = React.createClass({ }, async fetchCellData(query) { - if (!query.text) { + if (!query || !query.text) { return } diff --git a/ui/src/data_explorer/components/Visualization.js b/ui/src/data_explorer/components/Visualization.js index 4eb28b84ec..8a67135b68 100644 --- a/ui/src/data_explorer/components/Visualization.js +++ b/ui/src/data_explorer/components/Visualization.js @@ -48,7 +48,7 @@ const Visualization = React.createClass({ getInitialState() { const {queryConfigs, activeQueryIndex} = this.props - if (!queryConfigs.length) { + if (!queryConfigs.length || activeQueryIndex === null) { return { view: GRAPH, } @@ -61,12 +61,12 @@ const Visualization = React.createClass({ componentWillReceiveProps(nextProps) { const {queryConfigs, activeQueryIndex} = nextProps - if (!queryConfigs.length || activeQueryIndex === this.props.activeQueryIndex) { + if (!queryConfigs.length || activeQueryIndex === null || activeQueryIndex === this.props.activeQueryIndex) { return } const activeQuery = queryConfigs[activeQueryIndex] - if (activeQuery && activeQuery.rawText) { + if (activeQuery && typeof activeQuery.rawText === 'string') { return this.setState({view: TABLE}) } }, diff --git a/ui/src/data_explorer/containers/DataExplorer.js b/ui/src/data_explorer/containers/DataExplorer.js index 83dda828af..2b694f269c 100644 --- a/ui/src/data_explorer/containers/DataExplorer.js +++ b/ui/src/data_explorer/containers/DataExplorer.js @@ -57,7 +57,7 @@ const DataExplorer = React.createClass({ getInitialState() { return { - activeQueryIndex: 0, + activeQueryIndex: null, } }, From 13a136e2bde809f580c094bb02ab5a5965ce11f8 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Mon, 3 Apr 2017 13:05:06 -0700 Subject: [PATCH 39/81] Make query required in table and remove redundancy --- .../data_explorer/components/QueryBuilder.js | 12 +++---- ui/src/data_explorer/components/Table.js | 31 +++++++++---------- .../data_explorer/components/Visualization.js | 3 +- 3 files changed, 20 insertions(+), 26 deletions(-) diff --git a/ui/src/data_explorer/components/QueryBuilder.js b/ui/src/data_explorer/components/QueryBuilder.js index b8adeb832b..38322270e1 100644 --- a/ui/src/data_explorer/components/QueryBuilder.js +++ b/ui/src/data_explorer/components/QueryBuilder.js @@ -40,20 +40,16 @@ const QueryBuilder = React.createClass({ children: node, }, - handleSetActiveQueryIndex(index) { - this.props.setActiveQueryIndex(index) - }, - handleAddQuery() { const newIndex = this.props.queries.length this.props.actions.addQuery() - this.handleSetActiveQueryIndex(newIndex) + this.props.setActiveQueryIndex(newIndex) }, handleAddRawQuery() { const newIndex = this.props.queries.length this.props.actions.addQuery({rawText: ''}) - this.handleSetActiveQueryIndex(newIndex) + this.props.setActiveQueryIndex(newIndex) }, getActiveQuery() { @@ -99,7 +95,7 @@ const QueryBuilder = React.createClass({ }, renderQueryTabList() { - const {queries, activeQueryIndex, onDeleteQuery, timeRange} = this.props + const {queries, activeQueryIndex, onDeleteQuery, timeRange, setActiveQueryIndex} = this.props return (
@@ -113,7 +109,7 @@ const QueryBuilder = React.createClass({ key={i} queryIndex={i} query={q} - onSelect={this.handleSetActiveQueryIndex} + onSelect={setActiveQueryIndex} onDelete={onDeleteQuery} queryTabText={q.rawText || buildInfluxQLQuery(timeRange, q) || `Query ${i + 1}`} /> diff --git a/ui/src/data_explorer/components/Table.js b/ui/src/data_explorer/components/Table.js index c8d33f0ed5..9b10a96af9 100644 --- a/ui/src/data_explorer/components/Table.js +++ b/ui/src/data_explorer/components/Table.js @@ -35,7 +35,7 @@ const ChronoTable = React.createClass({ query: shape({ host: arrayOf(string.isRequired).isRequired, text: string.isRequired, - }), + }).isRequired, containerWidth: number.isRequired, height: number, onEditRawStatus: func.isRequired, @@ -54,6 +54,19 @@ const ChronoTable = React.createClass({ } }, + componentDidMount() { + this.fetchCellData(this.props.query) + }, + + componentWillReceiveProps(nextProps) { + if (this.props.query.text === nextProps.query.text) { + return + } + + this.fetchCellData(nextProps.query) + }, + + async fetchCellData(query) { if (!query || !query.text) { return @@ -93,22 +106,6 @@ const ChronoTable = React.createClass({ } }, - componentDidMount() { - this.fetchCellData(this.props.query) - }, - - componentWillReceiveProps(nextProps) { - if (!this.props.query || !nextProps.query) { - return - } - - if (this.props.query.text === nextProps.query.text) { - return - } - - this.fetchCellData(nextProps.query) - }, - handleColumnResize(newColumnWidth, columnKey) { this.setState(({columnWidths}) => ({ columnWidths: Object.assign({}, columnWidths, { diff --git a/ui/src/data_explorer/components/Visualization.js b/ui/src/data_explorer/components/Visualization.js index 8a67135b68..5d2c28d865 100644 --- a/ui/src/data_explorer/components/Visualization.js +++ b/ui/src/data_explorer/components/Visualization.js @@ -102,12 +102,13 @@ const Visualization = React.createClass({ renderVisualization(view, queries, heightPixels, onEditRawStatus, activeQueryIndex) { const activeQuery = queries[activeQueryIndex] const defaultQuery = queries[0] + const q = activeQuery || defaultQuery switch (view) { case GRAPH: return this.renderGraph(queries) case TABLE: - return (
) + return (q ?
: null) default: this.renderGraph(queries) } From ed2b83457c4efd0f179cdaafa40e51d44520e9f9 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Mon, 3 Apr 2017 13:26:47 -0700 Subject: [PATCH 40/81] Remove requirement from prop --- ui/src/data_explorer/components/Table.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/data_explorer/components/Table.js b/ui/src/data_explorer/components/Table.js index 9b10a96af9..acc09349be 100644 --- a/ui/src/data_explorer/components/Table.js +++ b/ui/src/data_explorer/components/Table.js @@ -38,7 +38,7 @@ const ChronoTable = React.createClass({ }).isRequired, containerWidth: number.isRequired, height: number, - onEditRawStatus: func.isRequired, + onEditRawStatus: func, }, getInitialState() { From d811faf67455fe9664c6796ae655703a6b91c919 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Mon, 3 Apr 2017 13:27:07 -0700 Subject: [PATCH 41/81] Improve no query state for table --- ui/src/data_explorer/components/Visualization.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/data_explorer/components/Visualization.js b/ui/src/data_explorer/components/Visualization.js index 5d2c28d865..be2de88f7a 100644 --- a/ui/src/data_explorer/components/Visualization.js +++ b/ui/src/data_explorer/components/Visualization.js @@ -108,7 +108,7 @@ const Visualization = React.createClass({ case GRAPH: return this.renderGraph(queries) case TABLE: - return (q ?
: null) + return (q ?
:
Enter your query below
) default: this.renderGraph(queries) } From 0983f1fade0c27bb5a59e0ca726f3be64ed8c9ed Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Mon, 3 Apr 2017 13:42:55 -0700 Subject: [PATCH 42/81] Improve copy --- ui/src/data_explorer/components/QueryBuilder.js | 6 +++--- ui/src/style/pages/data-explorer/query-builder.scss | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ui/src/data_explorer/components/QueryBuilder.js b/ui/src/data_explorer/components/QueryBuilder.js index 38322270e1..ac78e1458f 100644 --- a/ui/src/data_explorer/components/QueryBuilder.js +++ b/ui/src/data_explorer/components/QueryBuilder.js @@ -122,17 +122,17 @@ const QueryBuilder = React.createClass({ onChoose(item) { switch (item.text) { - case 'Query Builder': + case 'Help me build a query': this.handleAddQuery() break - case 'Query Editor': + case 'Type my own query': this.handleAddRawQuery() break } }, renderAddQuery() { - const items = [{text: 'Query Builder'}, {text: 'Query Editor'}] + const items = [{text: 'Help me build a query'}, {text: 'Type my own query'}] return ( diff --git a/ui/src/style/pages/data-explorer/query-builder.scss b/ui/src/style/pages/data-explorer/query-builder.scss index 98bdad1d63..ae5c63e4d3 100644 --- a/ui/src/style/pages/data-explorer/query-builder.scss +++ b/ui/src/style/pages/data-explorer/query-builder.scss @@ -115,9 +115,9 @@ } } > .dropdown-menu { - width: 108px !important; + width: 145px !important; min-width: 108px !important; - max-width: 108px !important; + max-width: 145px !important; } } .panel--tab-new.open { @@ -249,4 +249,4 @@ $query-builder--column-heading-height: 50px; .alert.alert-rawquery { border-color: $g6-smoke; color: $g12-forge; -} \ No newline at end of file +} From 006a69e1e5df83068a99277e81dc20fdd6cc99c3 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Mon, 3 Apr 2017 13:48:59 -0700 Subject: [PATCH 43/81] Fix portion of table being blocked from view --- ui/src/data_explorer/components/Table.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/data_explorer/components/Table.js b/ui/src/data_explorer/components/Table.js index acc09349be..8b10e72603 100644 --- a/ui/src/data_explorer/components/Table.js +++ b/ui/src/data_explorer/components/Table.js @@ -50,7 +50,7 @@ const ChronoTable = React.createClass({ getDefaultProps() { return { - height: 600, + height: 1000, } }, From 92c74050f47de84983689e96767bd466ef3229dc Mon Sep 17 00:00:00 2001 From: Luke Morris Date: Mon, 3 Apr 2017 13:49:35 -0700 Subject: [PATCH 44/81] If InfluxQL, use rawText for label and query --- ui/src/dashboards/components/CellEditorOverlay.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/dashboards/components/CellEditorOverlay.js b/ui/src/dashboards/components/CellEditorOverlay.js index e54abc11b7..a4556beffa 100644 --- a/ui/src/dashboards/components/CellEditorOverlay.js +++ b/ui/src/dashboards/components/CellEditorOverlay.js @@ -69,7 +69,7 @@ class CellEditorOverlay extends Component { newCell.type = cellWorkingType newCell.queries = queriesWorkingDraft.map((q) => { const query = q.rawText || buildInfluxQLQuery(timeRange, q) - const label = `${q.measurement}.${q.fields[0].field}` + const label = q.rawText || `${q.measurement}.${q.fields[0].field}` return { queryConfig: q, From e1d2949b18e8c6ae66ce0e545ab700ceaa5983fc Mon Sep 17 00:00:00 2001 From: Tim Raymond Date: Fri, 31 Mar 2017 11:20:44 -0400 Subject: [PATCH 45/81] Implement a MountableRouter The httprouter used in Chronograf did not support prefixing every route with some basepath. This caused problems for those using the --basepath parameter in combination with a load balancer that did not strip the basepath prefix from requests that it forwarded onto Chronograf. To support this, MountableRouter prefixes all routes at definition time with the supplied prefix. --- chronograf.go | 13 ++ server/mountable_router.go | 58 ++++++++ server/mountable_router_test.go | 240 ++++++++++++++++++++++++++++++++ 3 files changed, 311 insertions(+) create mode 100644 server/mountable_router.go create mode 100644 server/mountable_router_test.go diff --git a/chronograf.go b/chronograf.go index 83a246703b..33ab74f204 100644 --- a/chronograf.go +++ b/chronograf.go @@ -42,6 +42,19 @@ type Logger interface { Writer() *io.PipeWriter } +// Router is an abstracted Router based on the API provided by the +// julienschmidt/httprouter package. +type Router interface { + http.Handler + GET(string, http.HandlerFunc) + PATCH(string, http.HandlerFunc) + POST(string, http.HandlerFunc) + DELETE(string, http.HandlerFunc) + PUT(string, http.HandlerFunc) + + Handler(string, string, http.Handler) +} + // Assets returns a handler to serve the website. type Assets interface { Handler() http.Handler diff --git a/server/mountable_router.go b/server/mountable_router.go new file mode 100644 index 0000000000..387c0016b5 --- /dev/null +++ b/server/mountable_router.go @@ -0,0 +1,58 @@ +package server + +import ( + "net/http" + + "github.com/influxdata/chronograf" +) + +var _ chronograf.Router = &MountableRouter{} + +// MountableRouter is an implementation of a chronograf.Router which supports +// prefixing each route of a Delegated chronograf.Router with a prefix. +type MountableRouter struct { + Prefix string + Delegate chronograf.Router +} + +// DELETE defines a route responding to a DELETE request that will be prefixed +// with the configured route prefix +func (mr *MountableRouter) DELETE(path string, handler http.HandlerFunc) { + mr.Delegate.DELETE(mr.Prefix+path, handler) +} + +// GET defines a route responding to a GET request that will be prefixed +// with the configured route prefix +func (mr *MountableRouter) GET(path string, handler http.HandlerFunc) { + mr.Delegate.GET(mr.Prefix+path, handler) +} + +// POST defines a route responding to a POST request that will be prefixed +// with the configured route prefix +func (mr *MountableRouter) POST(path string, handler http.HandlerFunc) { + mr.Delegate.POST(mr.Prefix+path, handler) +} + +// PUT defines a route responding to a PUT request that will be prefixed +// with the configured route prefix +func (mr *MountableRouter) PUT(path string, handler http.HandlerFunc) { + mr.Delegate.PUT(mr.Prefix+path, handler) +} + +// PATCH defines a route responding to a PATCH request that will be prefixed +// with the configured route prefix +func (mr *MountableRouter) PATCH(path string, handler http.HandlerFunc) { + mr.Delegate.PATCH(mr.Prefix+path, handler) +} + +// Handler defines a prefixed route responding to a request type specified in +// the method parameter +func (mr *MountableRouter) Handler(method string, path string, handler http.Handler) { + mr.Delegate.Handler(method, mr.Prefix+path, handler) +} + +// ServeHTTP is an implementation of http.Handler which delegates to the +// configured Delegate's implementation of http.Handler +func (mr *MountableRouter) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + mr.Delegate.ServeHTTP(rw, r) +} diff --git a/server/mountable_router_test.go b/server/mountable_router_test.go new file mode 100644 index 0000000000..6c8b2bb392 --- /dev/null +++ b/server/mountable_router_test.go @@ -0,0 +1,240 @@ +package server_test + +import ( + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/http/httptest" + "strings" + "testing" + + "github.com/bouk/httprouter" + "github.com/influxdata/chronograf/server" +) + +func Test_MountableRouter_MountsRoutesUnderPrefix(t *testing.T) { + t.Parallel() + + mr := &server.MountableRouter{ + Prefix: "/chronograf", + Delegate: httprouter.New(), + } + + expected := "Hello?! McFly?! Anybody in there?!" + mr.GET("/biff", http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + fmt.Fprintf(rw, expected) + })) + + ts := httptest.NewServer(mr) + defer ts.Close() + + resp, err := http.Get(ts.URL + "/chronograf/biff") + if err != nil { + t.Fatal("Unexpected error fetching from mounted router: err:", err) + } + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + t.Fatal("Unexpected error decoding response body: err:", err) + } + + if resp.StatusCode != http.StatusOK { + t.Fatal("Expected 200 but received", resp.StatusCode) + } + + if string(body) != expected { + t.Fatalf("Unexpected response body: Want: \"%s\". Got: \"%s\"", expected, string(body)) + } +} + +func Test_MountableRouter_PrefixesPosts(t *testing.T) { + t.Parallel() + + mr := &server.MountableRouter{ + Prefix: "/chronograf", + Delegate: httprouter.New(), + } + + expected := "Great Scott!" + actual := make([]byte, len(expected)) + mr.POST("/doc", http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + defer r.Body.Close() + if _, err := io.ReadFull(r.Body, actual); err != nil { + rw.WriteHeader(http.StatusInternalServerError) + } else { + rw.WriteHeader(http.StatusOK) + } + })) + + ts := httptest.NewServer(mr) + defer ts.Close() + + resp, err := http.Post(ts.URL+"/chronograf/doc", "text/plain", strings.NewReader(expected)) + if err != nil { + t.Fatal("Unexpected error posting to mounted router: err:", err) + } + + if resp.StatusCode != http.StatusOK { + t.Fatal("Expected 200 but received", resp.StatusCode) + } + + if string(actual) != expected { + t.Fatalf("Unexpected request body: Want: \"%s\". Got: \"%s\"", expected, string(actual)) + } +} + +func Test_MountableRouter_PrefixesPuts(t *testing.T) { + t.Parallel() + + mr := &server.MountableRouter{ + Prefix: "/chronograf", + Delegate: httprouter.New(), + } + + expected := "Great Scott!" + actual := make([]byte, len(expected)) + mr.PUT("/doc", http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + defer r.Body.Close() + if _, err := io.ReadFull(r.Body, actual); err != nil { + rw.WriteHeader(http.StatusInternalServerError) + } else { + rw.WriteHeader(http.StatusOK) + } + })) + + ts := httptest.NewServer(mr) + defer ts.Close() + + req := httptest.NewRequest(http.MethodPut, ts.URL+"/chronograf/doc", strings.NewReader(expected)) + req.Header.Set("Content-Type", "text/plain; charset=utf-8") + req.Header.Set("Content-Length", fmt.Sprintf("%d", len(expected))) + req.RequestURI = "" + + client := http.Client{} + resp, err := client.Do(req) + if err != nil { + t.Fatal("Unexpected error posting to mounted router: err:", err) + } + + if resp.StatusCode != http.StatusOK { + t.Fatal("Expected 200 but received", resp.StatusCode) + } + + if string(actual) != expected { + t.Fatalf("Unexpected request body: Want: \"%s\". Got: \"%s\"", expected, string(actual)) + } +} + +func Test_MountableRouter_PrefixesDeletes(t *testing.T) { + t.Parallel() + + mr := &server.MountableRouter{ + Prefix: "/chronograf", + Delegate: httprouter.New(), + } + + mr.DELETE("/proto1985", http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + rw.WriteHeader(http.StatusNoContent) + })) + + ts := httptest.NewServer(mr) + defer ts.Close() + + req := httptest.NewRequest(http.MethodDelete, ts.URL+"/chronograf/proto1985", nil) + req.RequestURI = "" + + client := http.Client{} + resp, err := client.Do(req) + if err != nil { + t.Fatal("Unexpected error sending request to mounted router: err:", err) + } + + if resp.StatusCode != http.StatusNoContent { + t.Fatal("Expected 204 but received", resp.StatusCode) + } +} + +func Test_MountableRouter_PrefixesPatches(t *testing.T) { + t.Parallel() + + type Character struct { + Name string + Items []string + } + + mr := &server.MountableRouter{ + Prefix: "/chronograf", + Delegate: httprouter.New(), + } + + biff := Character{"biff", []string{"sports almanac"}} + mr.PATCH("/1955", http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + defer r.Body.Close() + c := Character{} + err := json.NewDecoder(r.Body).Decode(&c) + if err != nil { + rw.WriteHeader(http.StatusBadRequest) + } else { + biff.Items = c.Items + rw.WriteHeader(http.StatusOK) + } + })) + + ts := httptest.NewServer(mr) + defer ts.Close() + + r, w := io.Pipe() + go func() { + _ = json.NewEncoder(w).Encode(Character{"biff", []string{}}) + w.Close() + }() + + req := httptest.NewRequest(http.MethodPatch, ts.URL+"/chronograf/1955", r) + req.RequestURI = "" + + client := http.Client{} + resp, err := client.Do(req) + if err != nil { + t.Fatal("Unexpected error sending request to mounted router: err:", err) + } + + if resp.StatusCode != http.StatusOK { + t.Fatal("Expected 200 but received", resp.StatusCode) + } + + if len(biff.Items) != 0 { + t.Fatal("Failed to alter history, biff still has the sports almanac") + } +} + +func Test_MountableRouter_PrefixesHandler(t *testing.T) { + t.Parallel() + + mr := &server.MountableRouter{ + Prefix: "/chronograf", + Delegate: httprouter.New(), + } + + mr.Handler(http.MethodGet, "/recklessAmountOfPower", http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + rw.WriteHeader(http.StatusOK) + rw.Write([]byte("1.21 Gigawatts!")) + })) + + ts := httptest.NewServer(mr) + defer ts.Close() + + req := httptest.NewRequest(http.MethodGet, ts.URL+"/chronograf/recklessAmountOfPower", nil) + req.RequestURI = "" + + client := http.Client{} + resp, err := client.Do(req) + if err != nil { + t.Fatal("Unexpected error sending request to mounted router: err:", err) + } + + if resp.StatusCode != http.StatusOK { + t.Fatal("Expected 200 but received", resp.StatusCode) + } +} From 77ede663478c13656317a22a66909d8bb0a5fe0e Mon Sep 17 00:00:00 2001 From: Tim Raymond Date: Fri, 31 Mar 2017 16:14:46 -0400 Subject: [PATCH 46/81] Use MountableRouter when Basepath is set This breaks compatibility with the old behavior of --basepath, so this requires that proxies be configured to not modify routes forwarded to backends. The old behavior will be supported in a subsequent commit. --- server/mux.go | 22 +++++++++++++++++++--- server/server.go | 3 +++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/server/mux.go b/server/mux.go index 49c69cdc76..8f5bee049a 100644 --- a/server/mux.go +++ b/server/mux.go @@ -31,7 +31,7 @@ type MuxOpts struct { // NewMux attaches all the route handlers; handler returned servers chronograf. func NewMux(opts MuxOpts, service Service) http.Handler { - router := httprouter.New() + hr := httprouter.New() /* React Application */ assets := Assets(AssetsOpts{ @@ -42,13 +42,29 @@ func NewMux(opts MuxOpts, service Service) http.Handler { // Prefix any URLs found in the React assets with any configured basepath prefixedAssets := NewDefaultURLPrefixer(basepath, assets, opts.Logger) + // Set route prefix for all routes if basepath is present + var router chronograf.Router + if opts.Basepath != "" { + router = &MountableRouter{ + Prefix: opts.Basepath, + Delegate: hr, + } + } else { + router = hr + } + // Compress the assets with gzip if an accepted encoding compressed := gziphandler.GzipHandler(prefixedAssets) // The react application handles all the routing if the server does not // know about the route. This means that we never have unknown // routes on the server. - router.NotFound = compressed + hr.NotFound = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + if opts.Basepath != "" { + r.URL.Path = r.URL.Path[len(opts.Basepath):] + } + compressed.ServeHTTP(rw, r) + }) /* Documentation */ router.GET("/swagger.json", Spec()) @@ -178,7 +194,7 @@ func NewMux(opts MuxOpts, service Service) http.Handler { // AuthAPI adds the OAuth routes if auth is enabled. // TODO: this function is not great. Would be good if providers added their routes. -func AuthAPI(opts MuxOpts, router *httprouter.Router) (http.Handler, AuthRoutes) { +func AuthAPI(opts MuxOpts, router chronograf.Router) (http.Handler, AuthRoutes) { auth := oauth2.NewJWT(opts.TokenSecret) routes := AuthRoutes{} for _, pf := range opts.ProviderFuncs { diff --git a/server/server.go b/server/server.go index 4efb881234..a7a942655b 100644 --- a/server/server.go +++ b/server/server.go @@ -14,7 +14,9 @@ import ( "github.com/influxdata/chronograf" "github.com/influxdata/chronograf/bolt" + "github.com/influxdata/chronograf/canned" "github.com/influxdata/chronograf/influx" + "github.com/influxdata/chronograf/layouts" clog "github.com/influxdata/chronograf/log" "github.com/influxdata/chronograf/oauth2" "github.com/influxdata/chronograf/uuid" @@ -217,6 +219,7 @@ func (s *Server) Serve(ctx context.Context) error { Logger: logger, UseAuth: s.useAuth(), ProviderFuncs: providerFuncs, + Basepath: basepath, }, service) // Add chronograf's version header to all requests From 8fd7105d5dc301a7de5a72016d69de5c0dd30fef Mon Sep 17 00:00:00 2001 From: Luke Morris Date: Mon, 3 Apr 2017 17:22:48 -0700 Subject: [PATCH 47/81] rawText queries should have blank labels for now --- ui/src/dashboards/components/CellEditorOverlay.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ui/src/dashboards/components/CellEditorOverlay.js b/ui/src/dashboards/components/CellEditorOverlay.js index a4556beffa..ce9c7a3e50 100644 --- a/ui/src/dashboards/components/CellEditorOverlay.js +++ b/ui/src/dashboards/components/CellEditorOverlay.js @@ -69,7 +69,9 @@ class CellEditorOverlay extends Component { newCell.type = cellWorkingType newCell.queries = queriesWorkingDraft.map((q) => { const query = q.rawText || buildInfluxQLQuery(timeRange, q) - const label = q.rawText || `${q.measurement}.${q.fields[0].field}` + const label = q.rawText ? + "" : + `${q.measurement}.${q.fields[0].field}` return { queryConfig: q, From ea858c58cb3dae088a0d594bab5a6533d2a93b52 Mon Sep 17 00:00:00 2001 From: Luke Morris Date: Mon, 3 Apr 2017 17:26:35 -0700 Subject: [PATCH 48/81] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fcec9c37dd..93c164cbf5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ 1. [#1106](https://github.com/influxdata/chronograf/issues/1106): Fix obscured legends in dashboards 1. [#1051](https://github.com/influxdata/chronograf/issue/1051): Exit presentation mode when using the browser back button 1. [#1123](https://github.com/influxdata/chronograf/issue/1123): Widen single column results in data explorer + 1. [#1164](https://github.com/influxdata/chronograf/issue/1164): Restore ability to save raw queries to a Dashboard Cell ### Features 1. [#1112](https://github.com/influxdata/chronograf/pull/1112): Add ability to delete a dashboard From df1946900250e6147dbe1acb613382ac0a9e8f79 Mon Sep 17 00:00:00 2001 From: Tim Raymond Date: Mon, 3 Apr 2017 16:41:05 -0400 Subject: [PATCH 49/81] Add parameter to control mounting behavior Some load balancers will strip prefixes on their way to the chronograf backend, others won't. The "--prefix-routes" parameter forces all requests to the backend to have the prefix specified in "--basepath". Omitting it will only cause routes to be rewritten in rendered templates and assumes that the load balancer will remove the prefix. Use with Caddy ============== An easy way to test this out is using the free Caddy http server at http://caddyserver.com. This Caddyfile will work with the options `--basepath /chronograf --prefix-routes` set: ``` localhost:2020 { proxy /chronograf localhost:8888 log stdout } ``` This Caddyfile will work with only the option `--basepath /chronograf` set: ``` localhost:2020 { proxy /chronograf localhost:8888 { except /chronograf } log stdout } ``` --- server/mux.go | 17 ++++++++++------- server/server.go | 4 ++-- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/server/mux.go b/server/mux.go index 8f5bee049a..844c1b76bc 100644 --- a/server/mux.go +++ b/server/mux.go @@ -20,11 +20,12 @@ const ( // MuxOpts are the options for the router. Mostly related to auth. type MuxOpts struct { - Logger chronograf.Logger - Develop bool // Develop loads assets from filesystem instead of bindata - Basepath string // URL path prefix under which all chronograf routes will be mounted - UseAuth bool // UseAuth turns on Github OAuth and JWT - TokenSecret string + Logger chronograf.Logger + Develop bool // Develop loads assets from filesystem instead of bindata + Basepath string // URL path prefix under which all chronograf routes will be mounted + PrefixRoutes bool // Mounts all backend routes under route specified by the Basepath + UseAuth bool // UseAuth turns on Github OAuth and JWT + TokenSecret string ProviderFuncs []func(func(oauth2.Provider, oauth2.Mux)) } @@ -44,7 +45,7 @@ func NewMux(opts MuxOpts, service Service) http.Handler { // Set route prefix for all routes if basepath is present var router chronograf.Router - if opts.Basepath != "" { + if opts.Basepath != "" && opts.PrefixRoutes { router = &MountableRouter{ Prefix: opts.Basepath, Delegate: hr, @@ -60,7 +61,9 @@ func NewMux(opts MuxOpts, service Service) http.Handler { // know about the route. This means that we never have unknown // routes on the server. hr.NotFound = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { - if opts.Basepath != "" { + // The assets handler is always unaware of basepaths, so it needs to always + // be removed before sending requests to it + if opts.Basepath != "" && opts.PrefixRoutes { r.URL.Path = r.URL.Path[len(opts.Basepath):] } compressed.ServeHTTP(rw, r) diff --git a/server/server.go b/server/server.go index a7a942655b..bb77d6006c 100644 --- a/server/server.go +++ b/server/server.go @@ -14,9 +14,7 @@ import ( "github.com/influxdata/chronograf" "github.com/influxdata/chronograf/bolt" - "github.com/influxdata/chronograf/canned" "github.com/influxdata/chronograf/influx" - "github.com/influxdata/chronograf/layouts" clog "github.com/influxdata/chronograf/log" "github.com/influxdata/chronograf/oauth2" "github.com/influxdata/chronograf/uuid" @@ -71,6 +69,7 @@ type Server struct { ReportingDisabled bool `short:"r" long:"reporting-disabled" description:"Disable reporting of usage stats (os,arch,version,cluster_id,uptime) once every 24hr" env:"REPORTING_DISABLED"` LogLevel string `short:"l" long:"log-level" value-name:"choice" choice:"debug" choice:"info" choice:"error" default:"info" description:"Set the logging level" env:"LOG_LEVEL"` Basepath string `short:"p" long:"basepath" description:"A URL path prefix under which all chronograf routes will be mounted" env:"BASE_PATH"` + PrefixRoutes bool `long:"prefix-routes" description:"Force chronograf server to require that all requests to it are prefixed with the value set in --basepath" env:"PREFIX_ROUTES"` ShowVersion bool `short:"v" long:"version" description:"Show Chronograf version info"` BuildInfo BuildInfo Listener net.Listener @@ -220,6 +219,7 @@ func (s *Server) Serve(ctx context.Context) error { UseAuth: s.useAuth(), ProviderFuncs: providerFuncs, Basepath: basepath, + PrefixRoutes: s.PrefixRoutes, }, service) // Add chronograf's version header to all requests From 206a6bba338d30b9de3b9b8e04236d0f7f174bc5 Mon Sep 17 00:00:00 2001 From: Tim Raymond Date: Tue, 4 Apr 2017 10:21:18 -0400 Subject: [PATCH 50/81] Bypass URLPrefixer if http.Flusher is unavailable In certain situations, the http.ResponseWriter passed to the URLPrefixer may not be an http.Flusher. A simple case where this may occur is if the Prefixer has been wrapped up in a middleware where the above middleware wraps the ResponseWriter in a ResponseWriter that doesn't implement the Flush method. Previously, the Prefixer would error, which would cause the request to fail with a 500. Instead, the condition is logged and the request is passed unmodified to the next middleware in the chain. This effectively disables prefixing for requests where the above ResponseWriter is not an http.Flusher. Misc. Changes ============= - Some tests for "builders" were moved to server/builders_test.go to follow with convention. We've been naming files after different things under test and leaving the file matching the package name for support objects-in this case a mock logger was added to that file. --- server/builders_test.go | 30 +++++++++++++ server/server_test.go | 84 +++++++++++++++++++++++++++++-------- server/url_prefixer.go | 22 ++++++---- server/url_prefixer_test.go | 69 ++++++++++++++++++++++++++++++ 4 files changed, 178 insertions(+), 27 deletions(-) create mode 100644 server/builders_test.go diff --git a/server/builders_test.go b/server/builders_test.go new file mode 100644 index 0000000000..19fd0f9a27 --- /dev/null +++ b/server/builders_test.go @@ -0,0 +1,30 @@ +package server_test + +import ( + "testing" + + "github.com/influxdata/chronograf/server" +) + +func TestLayoutBuilder(t *testing.T) { + var l server.LayoutBuilder = &server.MultiLayoutBuilder{} + layout, err := l.Build(nil) + if err != nil { + t.Fatalf("MultiLayoutBuilder can't build a MultiLayoutStore: %v", err) + } + + if layout == nil { + t.Fatal("LayoutBuilder should have built a layout") + } +} + +func TestSourcesStoresBuilder(t *testing.T) { + var b server.SourcesBuilder = &server.MultiSourceBuilder{} + sources, err := b.Build(nil) + if err != nil { + t.Fatalf("MultiSourceBuilder can't build a MultiSourcesStore: %v", err) + } + if sources == nil { + t.Fatal("SourcesBuilder should have built a MultiSourceStore") + } +} diff --git a/server/server_test.go b/server/server_test.go index 829f53faf9..22330e851e 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -1,26 +1,74 @@ -package server +package server_test -import "testing" +import ( + "fmt" + "io" -func TestLayoutBuilder(t *testing.T) { - var l LayoutBuilder = &MultiLayoutBuilder{} - layout, err := l.Build(nil) - if err != nil { - t.Fatalf("MultiLayoutBuilder can't build a MultiLayoutStore: %v", err) - } + "github.com/influxdata/chronograf" +) - if layout == nil { - t.Fatal("LayoutBuilder should have built a layout") - } +type LogMessage struct { + Level string + Body string } -func TestSourcesStoresBuilder(t *testing.T) { - var b SourcesBuilder = &MultiSourceBuilder{} - sources, err := b.Build(nil) - if err != nil { - t.Fatalf("MultiSourceBuilder can't build a MultiSourcesStore: %v", err) +// TestLogger is a chronograf.Logger which allows assertions to be made on the +// contents of its messages. +type TestLogger struct { + Messages []LogMessage +} + +func (tl *TestLogger) Debug(args ...interface{}) { + tl.Messages = append(tl.Messages, LogMessage{"debug", tl.stringify(args...)}) +} + +func (tl *TestLogger) Info(args ...interface{}) { + tl.Messages = append(tl.Messages, LogMessage{"info", tl.stringify(args...)}) +} + +func (tl *TestLogger) Error(args ...interface{}) { + tl.Messages = append(tl.Messages, LogMessage{"error", tl.stringify(args...)}) +} + +func (tl *TestLogger) WithField(key string, value interface{}) chronograf.Logger { + return tl +} + +func (tl *TestLogger) Writer() *io.PipeWriter { + _, write := io.Pipe() + return write +} + +// HasMessage will return true if the TestLogger has been called with an exact +// match of a particular log message at a particular log level +func (tl *TestLogger) HasMessage(level string, body string) bool { + for _, msg := range tl.Messages { + if msg.Level == level && msg.Body == body { + return true + } } - if sources == nil { - t.Fatal("SourcesBuilder should have built a MultiSourceStore") + return false +} + +func (tl *TestLogger) stringify(args ...interface{}) string { + out := []byte{} + for _, arg := range args[:len(args)-1] { + out = append(out, tl.stringifyArg(arg)...) + out = append(out, []byte(" ")...) + } + out = append(out, tl.stringifyArg(args[len(args)-1])...) + return string(out) +} + +func (tl *TestLogger) stringifyArg(arg interface{}) []byte { + switch a := arg.(type) { + case fmt.Stringer: + return []byte(a.String()) + case error: + return []byte(a.Error()) + case string: + return []byte(a) + default: + return []byte("UNKNOWN") } } diff --git a/server/url_prefixer.go b/server/url_prefixer.go index 10387c08e6..0a58436460 100644 --- a/server/url_prefixer.go +++ b/server/url_prefixer.go @@ -9,6 +9,10 @@ import ( "github.com/influxdata/chronograf" ) +const ( + ErrNotFlusher = "Expected http.ResponseWriter to be an http.Flusher, but wasn't" +) + // URLPrefixer is a wrapper for an http.Handler that will prefix all occurrences of a relative URL with the configured Prefix type URLPrefixer struct { Prefix string // the prefix to be appended after any detected Attrs @@ -70,21 +74,21 @@ const ChunkSize int = 512 // stream through the ResponseWriter, and appending the Prefix after any of the // Attrs detected in the stream. func (up *URLPrefixer) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + // extract the flusher for flushing chunks + flusher, ok := rw.(http.Flusher) + + if !ok { + up.Logger.Info(ErrNotFlusher) + up.Next.ServeHTTP(rw, r) + return + } + // chunked transfer because we're modifying the response on the fly, so we // won't know the final content-length rw.Header().Set("Connection", "Keep-Alive") rw.Header().Set("Transfer-Encoding", "chunked") writtenCount := 0 // number of bytes written to rw - - // extract the flusher for flushing chunks - flusher, ok := rw.(http.Flusher) - if !ok { - msg := "Expected http.ResponseWriter to be an http.Flusher, but wasn't" - Error(rw, http.StatusInternalServerError, msg, up.Logger) - return - } - nextRead, nextWrite := io.Pipe() go func() { defer nextWrite.Close() diff --git a/server/url_prefixer_test.go b/server/url_prefixer_test.go index 33624c1487..619b304d2c 100644 --- a/server/url_prefixer_test.go +++ b/server/url_prefixer_test.go @@ -106,3 +106,72 @@ func Test_Server_Prefixer_RewritesURLs(t *testing.T) { } } } + +// clogger is an http.ResponseWriter that is not an http.Flusher. It is used +// for testing the behavior of handlers that may rely on specific behavior of +// http.Flusher +type clogger struct { + next http.ResponseWriter +} + +func (c *clogger) Header() http.Header { + return c.next.Header() +} + +func (c *clogger) Write(bytes []byte) (int, error) { + return c.next.Write(bytes) +} + +func (c *clogger) WriteHeader(code int) { + c.next.WriteHeader(code) +} + +func Test_Server_Prefixer_NoPrefixingWithoutFlusther(t *testing.T) { + backend := http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + fmt.Fprintf(rw, "Hill Valley Preservation Society") + }) + + wrapFunc := func(next http.Handler) http.Handler { + return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + clog := &clogger{rw} + next.ServeHTTP(clog, r) + }) + } + + tl := &TestLogger{} + pfx := &server.URLPrefixer{ + Prefix: "/hill", + Next: backend, + Logger: tl, + Attrs: [][]byte{ + []byte("href=\""), + }, + } + + ts := httptest.NewServer(wrapFunc(pfx)) + defer ts.Close() + + res, err := http.Get(ts.URL) + if err != nil { + t.Fatal("Unexpected error fetching from prefixer: err:", err) + } + + actual, err := ioutil.ReadAll(res.Body) + if err != nil { + t.Fatal("Unable to read prefixed body: err:", err) + } + + unexpected := "Hill Valley Preservation Society" + expected := "Hill Valley Preservation Society" + if string(actual) == unexpected { + t.Error("No Flusher", ":\n Prefixing occurred without an http.Flusher") + } + + if string(actual) != expected { + t.Error("No Flusher", ":\n\tPrefixing failed to output without an http.Flusher\n\t\tWant:\n", expected, "\n\t\tGot:\n", string(actual)) + } + + if !tl.HasMessage("info", server.ErrNotFlusher) { + t.Error("No Flusher", ":\n Expected Error Message: \"", server.ErrNotFlusher, "\" but saw none. Msgs:", tl.Messages) + } +} From 1376f522b6650711e728bed8331bc3e086d7178d Mon Sep 17 00:00:00 2001 From: Tim Raymond Date: Tue, 4 Apr 2017 10:59:16 -0400 Subject: [PATCH 51/81] Update CHANGELOG with --prefix-routes changes --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fcec9c37dd..9c9fdf16d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ 1. [#1120](https://github.com/influxdata/chronograf/pull/1120): Allow users to update user passwords. 1. [#1129](https://github.com/influxdata/chronograf/pull/1129): Allow InfluxDB and Kapacitor configuration via ENV vars or CLI options 1. [#1130](https://github.com/influxdata/chronograf/pull/1130): Add loading spinner to Alert History page. + 1. [#1168](https://github.com/influxdata/chronograf/issue/1168): Expand support for --basepath on some load balancers ### UI Improvements 1. [#1101](https://github.com/influxdata/chronograf/pull/1101): Compress InfluxQL responses with gzip From 08f2489bc0868367ff0c6f9c1bcc057d3c0e1711 Mon Sep 17 00:00:00 2001 From: Tim Raymond Date: Tue, 4 Apr 2017 11:03:07 -0400 Subject: [PATCH 52/81] Update CHANGELOG with URL Prefixer Bypass changes [ci skip] --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fcec9c37dd..87046de3d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ 1. [#1106](https://github.com/influxdata/chronograf/issues/1106): Fix obscured legends in dashboards 1. [#1051](https://github.com/influxdata/chronograf/issue/1051): Exit presentation mode when using the browser back button 1. [#1123](https://github.com/influxdata/chronograf/issue/1123): Widen single column results in data explorer + 1. [#1115](https://github.com/influxdata/chronograf/pull/1115): Fix Basepath issue where content would fail to render under certain circumstances ### Features 1. [#1112](https://github.com/influxdata/chronograf/pull/1112): Add ability to delete a dashboard From d04483f779fdfd3717f87a2643fc7d3ffc60a5e0 Mon Sep 17 00:00:00 2001 From: Tim Raymond Date: Tue, 4 Apr 2017 14:03:46 -0400 Subject: [PATCH 53/81] Favor http.StripPrefix over home-rolled version http.StripPrefix is a standard library handler which is designed to do exactly what the inline http.HandlerFunc did (with almost the same implementation). --- server/mux.go | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/server/mux.go b/server/mux.go index 844c1b76bc..95d1741cb7 100644 --- a/server/mux.go +++ b/server/mux.go @@ -43,6 +43,9 @@ func NewMux(opts MuxOpts, service Service) http.Handler { // Prefix any URLs found in the React assets with any configured basepath prefixedAssets := NewDefaultURLPrefixer(basepath, assets, opts.Logger) + // Compress the assets with gzip if an accepted encoding + compressed := gziphandler.GzipHandler(prefixedAssets) + // Set route prefix for all routes if basepath is present var router chronograf.Router if opts.Basepath != "" && opts.PrefixRoutes { @@ -50,25 +53,16 @@ func NewMux(opts MuxOpts, service Service) http.Handler { Prefix: opts.Basepath, Delegate: hr, } + // The react application handles all the routing if the server does not + // know about the route. This means that we never have unknown routes on + // the server. The assets handler is always unaware of basepaths, so the + // basepath needs to always be removed before sending requests to it + hr.NotFound = http.StripPrefix(opts.Basepath, compressed) } else { router = hr + hr.NotFound = compressed } - // Compress the assets with gzip if an accepted encoding - compressed := gziphandler.GzipHandler(prefixedAssets) - - // The react application handles all the routing if the server does not - // know about the route. This means that we never have unknown - // routes on the server. - hr.NotFound = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { - // The assets handler is always unaware of basepaths, so it needs to always - // be removed before sending requests to it - if opts.Basepath != "" && opts.PrefixRoutes { - r.URL.Path = r.URL.Path[len(opts.Basepath):] - } - compressed.ServeHTTP(rw, r) - }) - /* Documentation */ router.GET("/swagger.json", Spec()) router.GET("/docs", Redoc("/swagger.json")) From 3c431d000a5759d546086a01c83918364828ac17 Mon Sep 17 00:00:00 2001 From: Alex P Date: Tue, 4 Apr 2017 12:22:23 -0700 Subject: [PATCH 54/81] Remove unused webpack files. -H --- ui/webpack/ignoreForTestLoader.js | 24 ------------- ui/webpack/testConfig.js | 60 ------------------------------- 2 files changed, 84 deletions(-) delete mode 100644 ui/webpack/ignoreForTestLoader.js delete mode 100644 ui/webpack/testConfig.js diff --git a/ui/webpack/ignoreForTestLoader.js b/ui/webpack/ignoreForTestLoader.js deleted file mode 100644 index 93e8ada8f4..0000000000 --- a/ui/webpack/ignoreForTestLoader.js +++ /dev/null @@ -1,24 +0,0 @@ -/* eslint-disable */ - -// Used for test only: use /ignore as a path for ignored resources (fonts, images, etc.). -// (See testem.json for how /ignore is handled.) - -const moduleSource = "module.exports = '/ignore';"; - -module.exports = function() { - if (this.cacheable) { - this.cacheable(); - } - - return moduleSource; -}; - -// Tells webpack not to bother with other loaders in this chain. -// See https://github.com/webpack/null-loader/blob/master/index.js -module.exports.pitch = function() { - if (this.cacheable) { - this.cacheable(); - } - - return moduleSource; -}; diff --git a/ui/webpack/testConfig.js b/ui/webpack/testConfig.js deleted file mode 100644 index cb62021d44..0000000000 --- a/ui/webpack/testConfig.js +++ /dev/null @@ -1,60 +0,0 @@ -var path = require('path'); -var hostname = 'localhost'; -var port = 7357; - -module.exports = { - devtool: 'eval', - entry: 'mocha!./spec/index.js', - output: { - filename: 'test.build.js', - path: 'spec/', - publicPath: 'http://' + hostname + ':' + port + '/spec' - }, - module: { - loaders: [ - { - test: /\.js$/, - exclude: /node_modules/, - loader: 'babel-loader' - }, - { - test: /\.css/, - exclude: /node_modules/, - loader: 'style-loader!css-loader!postcss-loader', - }, - { - test: /\.scss/, - exclude: /node_modules/, - loader: 'style-loader!css-loader!sass-loader', - }, - { // Sinon behaves weirdly with webpack, see https://github.com/webpack/webpack/issues/304 - test: /sinon\/pkg\/sinon\.js/, - loader: 'imports?define=>false,require=>false', - }, - { - test: /\.json$/, - loader: 'json', - }, - ] - }, - externals: { - 'react/addons': true, - 'react/lib/ExecutionEnvironment': true, - 'react/lib/ReactContext': true - }, - devServer: { - host: hostname, - port: port, - }, - resolve: { - alias: { - app: path.resolve(__dirname, '..', 'app'), - src: path.resolve(__dirname, '..', 'src'), - chronograf: path.resolve(__dirname, '..', 'src', 'chronograf'), - shared: path.resolve(__dirname, '..', 'src', 'shared'), - style: path.resolve(__dirname, '..', 'src', 'style'), - utils: path.resolve(__dirname, '..', 'src', 'utils'), - sinon: 'sinon/pkg/sinon', - } - } -}; From 65d9ac67e0211b58bd2f920ad0c1fd27d3dc781f Mon Sep 17 00:00:00 2001 From: Alex P Date: Tue, 4 Apr 2017 12:22:47 -0700 Subject: [PATCH 55/81] Use more supported .cur format instead of .png --- ui/assets/images/cursor-default.cur | Bin 0 -> 1014 bytes ui/assets/images/cursor-ew-resize.cur | Bin 0 -> 1162 bytes ui/assets/images/cursor-invert.cur | Bin 0 -> 2162 bytes ui/assets/images/cursor-move.cur | Bin 0 -> 3086 bytes ui/assets/images/cursor-nesw-resize.cur | Bin 0 -> 1430 bytes ui/assets/images/cursor-ns-resize.cur | Bin 0 -> 1214 bytes ui/assets/images/cursor-nwse-resize.cur | Bin 0 -> 1430 bytes ui/assets/images/cursor-pointer.cur | Bin 0 -> 1574 bytes ui/assets/images/cursor-text.cur | Bin 0 -> 1022 bytes ui/src/style/modules/custom-cursors.scss | 18 +++++++++--------- ui/webpack/devConfig.js | 2 +- ui/webpack/prodConfig.js | 2 +- 12 files changed, 11 insertions(+), 11 deletions(-) create mode 100644 ui/assets/images/cursor-default.cur create mode 100644 ui/assets/images/cursor-ew-resize.cur create mode 100644 ui/assets/images/cursor-invert.cur create mode 100644 ui/assets/images/cursor-move.cur create mode 100644 ui/assets/images/cursor-nesw-resize.cur create mode 100644 ui/assets/images/cursor-ns-resize.cur create mode 100644 ui/assets/images/cursor-nwse-resize.cur create mode 100644 ui/assets/images/cursor-pointer.cur create mode 100644 ui/assets/images/cursor-text.cur diff --git a/ui/assets/images/cursor-default.cur b/ui/assets/images/cursor-default.cur new file mode 100644 index 0000000000000000000000000000000000000000..4316cf736569df31fbe1d8bc819ba0b883e79b1c GIT binary patch literal 1014 zcma)*YexK6Tv9zR|K~jKIse~z&WHD9Oh8ZK zaz?w5jU_Oa!Wfg05=kNw&+~PdYAcq86-v-{e4V%@dgjq65~b-gM31%UnKPO+;kD{i zAyIReC0~M*8zg5;O0j@f8su452KMj54@Wa>s$%F?Wvb^AjJ0i&;c7)8S~g1U<~&J? zC0{oEy{7@_HN#P+!V&T6o$<`MEpi0z8PHu-h}L4+XuoDV;68@0T}GIclBvd=ti|!n z#gc4bSdYL%9b9{g;OjjI+^@$SV;N{}YrLPoJImJsuMPn(wD{@O0$u}V9_evmhXOSB zx6?&ZKh5oL&Xf30=xUK9ILi{OYB0Z$DA4|FtlE$$vI;d75z z3#94%p1!?G;6o$uz7BY2Aif58L;NVk=Q?<-r8vJ;{;Xs3nq=IrIjZBAZvaE>Row6Tmp^*XptYR)yGVb0!_nTck$l|W`M>_iJ$CYk{)5>~y7+XeSQB6nOizud&3L-Vi3rGp1g`^}>21!Nw4g3yCh5!Hn literal 0 HcmV?d00001 diff --git a/ui/assets/images/cursor-ew-resize.cur b/ui/assets/images/cursor-ew-resize.cur new file mode 100644 index 0000000000000000000000000000000000000000..eb60e89f2b1ad5f2f2f4caa195ff68c95d3da18b GIT binary patch literal 1162 zcmcJPO-NKx6o7BoE*BRAnTzRY zrsVEc;iF1rDy8xzU6Q76#!a|oE<@Itbc(3|RC>>8YrOYdo_1Pi&n2#ujLim1)9q)c z_IlAtOZe}E&p!AF|E;9u#O|<8)JA-H-(N*8(^u}<7e6`E&U`4aDK#$ctP#q}Nek!X z-o6{~k&Ly%bkyc&_gCcWfvN)isJc);ttrwkE_w7weX$;I@aoA%pPp*=Yw-hLhS+=) zW2y7Jd6!#~X{JulIgYZH6AG;uV6V`j-(*yV%-{$jmia#3(7Q*GG#^PT4-|~<6LHrus3;i^A1DYc@70tg#%AxR!C59zzW9rAWd_#l_;l?_17w!n_q6Ndqa zb8%1f7vly~m?=X+mK^!J*)KA{M3a$3JV)eqB!e>^Sm}uJH`ORxXT<(@hNGkU3QD%dp=eto3U@3=MSd1+ z&A;J74`6M)jr_bgI>Lz;5$`?NSGCv)hn9Mwaz!XAR!zdyE}*mOI!d=}W#$~T*Zh>T zel>huT+vfe4AdSu!TQC}V*&1F@Zh7JDF{=#VM@?2Jh)^9%F;_wl)eT<+hb9%GYt8=bgZ8XGmEt$$Eipa zPD~6$UCdO}PEW#(c1(5iLY&?oSL#c#IAScux@b_5vYzu$aSLS|ideri?0a_sO47!& ze*P%PR3krI!TQP37^=dV5oVl179)U zm*flbx%E$cdioxY<;1amo+#d?muA%2)k?`(Q!F|)>Mv%GUyLV5J7-NeYy$m!r#B zg-$m!+C402(D#W7IeBMqqhZb>x1!@w;5}^g0NB-h}Y?E$xQ*iuDp+? zY1ypbeALH`XZ^S?W@=D9TY;mAa#Ssna86PYGi-7TdK@+jU#f2yw)li%6TKUJg7HcD z4&b}ud$5jr4^5%-aC+o;G(?ziVuBj=(F)W~k)y^U;aL1*-#ICk-i^YSX?zA5hH#|& zkHE!)JAs>f@1iRp16H3zwEB(1*#Hxog4JjYSD<0E94E&~IR31Jy2+1xfpkqHNy4|+ ze>C<4jKS5KT)MJ;g-x>+U8=e0P)DJCh(D~}YSvGI=Am-&JBr3IiJ2v?W#MxnL&+qv zT=)uyMd6#{dBBZ~U+}Rq1?OZ5u-QkVTj7UpXLr_5few#8SD&xNPeOB`gr*STb0$U- zMG}PX2P?m8H`4Fmyh9SN%^_on5jL4S>*vUM{@c~(Yu8G!>Lspa;d3HhWH^ZtK3q?| ziSvquJfA@#hhsy` zB(6D$Yfkv=iI_tV;zI)Z5q=#JE(anfqW_bz$;Ca{C}>;u7TNw_n!>B|5Rzh_!4B)MA?dV-xpB5z)7oe;9}z RQ4n|HPbQJnUa1Ye(qA~3E^Yt- literal 0 HcmV?d00001 diff --git a/ui/assets/images/cursor-move.cur b/ui/assets/images/cursor-move.cur new file mode 100644 index 0000000000000000000000000000000000000000..49f38f746e74269cb174e20349a8328d43d3e8d9 GIT binary patch literal 3086 zcmcJRU1$_{6vfA9A1ZzDCBZ~@)+S~r*~Az>C=nDy6jAUiD9UOhrZkHmiN@4cqfx|$ zswf)K5?f+W{J?06nkXey1Qj)c>w~V=(TvzKrJcnG*91JCJ7#sV%q(k6GMt_JzxUkp zW7*vyiZYB>Mux)IY$ZNiQAR3?l0&hjlu*)_zjDsL?33LXz{{G<(JIwNX#-eKd!;IS zj9Q&N#%P>ciVdTECeK5{tMU8PO@5zoBfJu5+JmmD3e=8OP4)+sw>ei;f5=sho9kBq z#~VKAUhQi9IL>c!J}A7cnqQ@i>+6=_b@Mh`IJFD$=IwTUYvXd%X?~NQ&*Ie=`ZK8r zWjO@2?m)xjJg^sY^rNhOX@B@>7z2VE?oL zSi7>=&+^)L_V-p99I_m5lVILCD#Y0~S_GvMz={82RttX)~`dp=JSjCAfc>z^jFCVR5d$w~Gw*GEMu z%OSvQNw8v$euQ;qa>l2exvzCp7Mag4)}q&Fv4wxnp!dc5T0g%*-k(ABd+vP-!|4^? zm2k27vU4Z!U)u+Dw{Jm5<@iD4B$yw0KLP)Dpu4?(NI1#7+dnTFQjU|y_t-U%&j&H+ z_SLynN8MB%N|yds`9g;Uz1Qm1Us-STZY!=|v*MvTrSG5hK5)Gi&z8Q-#V3B*v-g=} K=Si+yENgU literal 0 HcmV?d00001 diff --git a/ui/assets/images/cursor-nesw-resize.cur b/ui/assets/images/cursor-nesw-resize.cur new file mode 100644 index 0000000000000000000000000000000000000000..c4fe030c1798cc3336822e7abf3cee363a5bd5a5 GIT binary patch literal 1430 zcmbu7Pe@cz6vodW(ZWSCi#CBa>e!?D$%Kkf z6yZQIkD)><`a`;qhYP`iRxyhTF>qJuHs%d}-tXy^L7nLhy~nxdz4Pw4iGTu)cL+KGK30SNz7*)w*4**_#c!v}CjB zg*wQve)MDE?rFTf9x&eC2yC;+P=V}qdp*+&b&!!+ir`bM4Iet{jhW7dA~F`EXW)cG zHoZ^>8JV9kd>KE_wJ9>JmAGRLS@c33WMozz;(M~)X5jbpf;ePtc}bChUkQ+bKhp(s zbU)}tGqC!uAWkj@)@B@Y$WUR=&y^);iog`tR`6Nws0f-8C^Kc?fGL^%VOduvhKB@U Nd%9p*Wrn#~+W;>Jb}|3} literal 0 HcmV?d00001 diff --git a/ui/assets/images/cursor-ns-resize.cur b/ui/assets/images/cursor-ns-resize.cur new file mode 100644 index 0000000000000000000000000000000000000000..ffdcd04c1fde7652a3a6ef2beca1f9210a82eef4 GIT binary patch literal 1214 zcmaizPe>GD7{))bn;t|#yhLm2x~;lSn~91@=)n$xdZ?fTI|w2QORNy(A3|LX3E zEI}74H^D#;x+}JL6FdkicykTgC^B22ON39)E9)@3y3;rO=6&Ahedc3e=2I$(S4x)R z-J;%KP%2X?Wg{s>BjU@laF$L}NVHrRvolDLq$kRV-4L2kpd3Jt%fdx6=(lPW#^dSkv%%d>h)Y)9d77qHN$BR&0y%q2fq zVofF68$(S%fP2OvKIdDK8!pfH5g5VhV~kc8#(1317~e(o^C}y#_7G#WMW9ZLpBwv^ zHCKQQHzw*Fpf=6Ex@iY?T$pThfZ8HVeu-}ilmTRCk&CWF!1L0vTv|b0bX?DIv1HQh%Vn->cW3r2FSJUDE zzD;2HK^f>@GS2+%P9MRFyIcp~z5L8K^j_vMc*Zv zbN{E#O35vegl99F&tN7<&Yby3NU2bi;C-c@Sd_kgg_QaqdYD3sdm_?hQnsjVZn?`A&2$>-$DNzvwEutV23=Kh4Bxwd=QYKV{ zB8diyc?=cyK_8?G5f?!Ptzs4xV&KlSt@wsc_xzm~!^?4=G{box_uc#Boco?qKKc9o zN{;(gbemGUl~N&*lvJsRb^dnAGyjftnSzJIaLqwq{Ak(GU{w&&a1a9(rRWcppf^~8 z?!W4SV-Tl3t5wwPGIf%0LXGOa$jCJcN)v}c*7yiAtn#!#=ox+&w23ho#%V>7b_qZA7;h9REJXL8;_Z)})={m@wFJ^S+xO;vR&b|Ab zJ|EVpbBC>&8}+ciw1X`AVn%l_S<~05hurnai%0a63t?+6)(HD+2gsr?W_0&D_rgIw z>(!NresMWsy}#8A`+G0QqAzB2W;QyL^C5oHu%_Cp_1m^8>tmu7+4&gAqAzCLXg0q_ zj=h+4Seegbr;+`U09o|KjQRbAuF1jA$AcFS;p`0bfpLSZaR&){J%X literal 0 HcmV?d00001 diff --git a/ui/assets/images/cursor-pointer.cur b/ui/assets/images/cursor-pointer.cur new file mode 100644 index 0000000000000000000000000000000000000000..bc164824e87a7b2f79ecbdf0657f1157a265dfbb GIT binary patch literal 1574 zcmc)JZ%7ky7zgm*h$5&Lr%>z5EbhWk%h(5ksGq8OgUc|8AKTt z6hSXcsa~vb)RxrhRlUiS$gnrH$Q4#z=k84i@$Gx(Hm-4w?Nu9}yXW`2=lgKGZ9>@i zE?OXXTq>kFLM#+QIH-t9NZ;3A-Lr)#o;S_dusHY^Et3d=#ksNPrA6G>3a(f#wK*L? zUuRC&wp4??o2=%vI9BevvAr4yb>uYIQU~_-WY3UG?ek@qJrTd&2t?X*n(A-G`O1>t z%oLmFbd;~U@UVUx@cdj}9yRO$Gye39i&d-bS2jDRv?qSx)#?j8qoqv;u!YaG$n zmv#Ze%>`i&`i$40tnZa}V>lx4%?X2hU*g z%~|w&Hz0Jh0!Z*TN?)Jx*`#XozPV-|p~Ef@DfE+?!rn^Y+f~C%b*RpT$)R=wGvCwS zt$q&Imq}6IdSLvP!CZ%rl_B}I)4)EBl$5>Z8POAN>1m(}_|a>KHn^dUbRqe!3%sV0 zlHxJv%=JK{TZ%RF|2FZ!z%@uEg=WlI&iEAtNS|T8o=?pT?_(pN#ti>)hHb!Cu4o_n zkbK{xf0prl?mxSSIS);w?9RS!wN`DiP{p)38+0#HWS*^Fac;(E$s)dl@Gq6?ziy$- A-2eap literal 0 HcmV?d00001 diff --git a/ui/assets/images/cursor-text.cur b/ui/assets/images/cursor-text.cur new file mode 100644 index 0000000000000000000000000000000000000000..51d8adf631e665d942c087948b85ece1cd030c3e GIT binary patch literal 1022 zcma))J4hT+6o!waTmwT`0@`TsEo#=?;HwbXCV_xrp$H;~fe;^{8xa&#L{V5RMy<9; zEPQ~t79xa*FGR4^%9MgDB7<0nAf!-!&$-OiJBl%P_~t+7KR79)I>rpWSsB0&TX8Wlgr(Zg;@yf$uV<1Q zEXY)h1adJ~6UJ3+2#5XOluADw$hptxvy6taUo+#2^6o*>@H-#+Si!pfHrc;?Bjwgm9I?Sle|Zfr!#R4qQ^$*#2q zEf?UVADfXnu(5&i4q1o=|m=%Jkv<_@wx-aB4;uH literal 0 HcmV?d00001 diff --git a/ui/src/style/modules/custom-cursors.scss b/ui/src/style/modules/custom-cursors.scss index 169768052b..789479197d 100644 --- a/ui/src/style/modules/custom-cursors.scss +++ b/ui/src/style/modules/custom-cursors.scss @@ -3,15 +3,15 @@ ---------------------------------------------- */ -$cc-default: url(assets/images/cursor-default.png), auto !important; -$cc-invert: url(assets/images/cursor-invert.png) 4 4, auto !important; -$cc-pointer: url(assets/images/cursor-pointer.png) 6 0, auto !important; -$cc-text: url(assets/images/cursor-text.png) 6 10, auto !important; -$cc-move: url(assets/images/cursor-move.png) 13 13, auto !important; -$cc-resize-ns: url(assets/images/cursor-ns-resize.png) 6 12, auto !important; -$cc-resize-ew: url(assets/images/cursor-ew-resize.png) 12 5, auto !important; -$cc-resize-nwse: url(assets/images/cursor-nwse-resize.png) 9 9, auto !important; -$cc-resize-nesw: url(assets/images/cursor-nesw-resize.png) 9 9, auto !important; +$cc-default: url(assets/images/cursor-default.cur), auto !important; +$cc-invert: url(assets/images/cursor-invert.cur) 4 4, auto !important; +$cc-pointer: url(assets/images/cursor-pointer.cur) 6 0, auto !important; +$cc-text: url(assets/images/cursor-text.cur) 6 10, auto !important; +$cc-move: url(assets/images/cursor-move.cur) 13 13, auto !important; +$cc-resize-ns: url(assets/images/cursor-ns-resize.cur) 6 12, auto !important; +$cc-resize-ew: url(assets/images/cursor-ew-resize.cur) 12 5, auto !important; +$cc-resize-nwse: url(assets/images/cursor-nwse-resize.cur) 9 9, auto !important; +$cc-resize-nesw: url(assets/images/cursor-nesw-resize.cur) 9 9, auto !important; // Resetting defaults diff --git a/ui/webpack/devConfig.js b/ui/webpack/devConfig.js index a90e8b97b8..c8b8fd14c1 100644 --- a/ui/webpack/devConfig.js +++ b/ui/webpack/devConfig.js @@ -46,7 +46,7 @@ module.exports = { loader: ExtractTextPlugin.extract('style-loader', 'css-loader!postcss-loader'), }, { - test : /\.(ico|png|jpg|ttf|eot|svg|woff(2)?)(\?[a-z0-9]+)?$/, + test : /\.(ico|png|cur|jpg|ttf|eot|svg|woff(2)?)(\?[a-z0-9]+)?$/, loader : 'file', }, { diff --git a/ui/webpack/prodConfig.js b/ui/webpack/prodConfig.js index 8b9abe10b6..51b405526d 100644 --- a/ui/webpack/prodConfig.js +++ b/ui/webpack/prodConfig.js @@ -49,7 +49,7 @@ var config = { loader: ExtractTextPlugin.extract('style-loader', 'css-loader!postcss-loader'), }, { - test : /\.(ico|png|jpg|ttf|eot|svg|woff(2)?)(\?[a-z0-9]+)?$/, + test : /\.(ico|png|cur|jpg|ttf|eot|svg|woff(2)?)(\?[a-z0-9]+)?$/, loader : 'file', }, { From 23478fffa7b0f3910c6bd3a87e28a83fb98738ab Mon Sep 17 00:00:00 2001 From: Jared Scheib Date: Tue, 4 Apr 2017 12:28:01 -0700 Subject: [PATCH 56/81] Fix saving email in Kapacitor alerts (#1173) * Fix Kapacitor Rules bug to now save user input data on an Alert Message * Update changelog --- CHANGELOG.md | 1 + ui/src/kapacitor/components/RuleMessageAlertConfig.js | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a51db2a125..c0c12eca8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ 1. [#1123](https://github.com/influxdata/chronograf/issues/1123): Widen single column results in data explorer 1. [#1164](https://github.com/influxdata/chronograf/pull/1164): Restore ability to save raw queries to a Dashboard Cell 1. [#1115](https://github.com/influxdata/chronograf/pull/1115): Fix Basepath issue where content would fail to render under certain circumstances + 1. [#1173](https://github.com/influxdata/chronograf/pull/1173): Fix saving email in Kapacitor alerts ### Features 1. [#1112](https://github.com/influxdata/chronograf/pull/1112): Add ability to delete a dashboard diff --git a/ui/src/kapacitor/components/RuleMessageAlertConfig.js b/ui/src/kapacitor/components/RuleMessageAlertConfig.js index eb95b297da..0bdbb0134a 100644 --- a/ui/src/kapacitor/components/RuleMessageAlertConfig.js +++ b/ui/src/kapacitor/components/RuleMessageAlertConfig.js @@ -25,8 +25,7 @@ const RuleMessageAlertConfig = ({ className="form-control size-486" type="text" placeholder={DEFAULT_ALERT_PLACEHOLDERS[alert]} - name="alertProperty" - onChange={(evt) => updateAlertNodes(rule.id, alert, evt.target.form.alertProperty.value)} + onChange={(e) => updateAlertNodes(rule.id, alert, e.target.value)} value={ALERT_NODES_ACCESSORS[alert](rule)} /> From 8a51adbced43aed0e0e677b16732a3a644e6cb29 Mon Sep 17 00:00:00 2001 From: Tim Raymond Date: Tue, 4 Apr 2017 15:59:09 -0400 Subject: [PATCH 57/81] Remove unnecessary conditional tests Re-mounting should only happen if the --prefix-routes option is set. If this happens, the result will be a no-op as intended since the --basepath will be "". MountableRouter and http.StripPrefix are both no-ops with prefix set to "" --- server/mux.go | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/server/mux.go b/server/mux.go index 95d1741cb7..a32345e103 100644 --- a/server/mux.go +++ b/server/mux.go @@ -46,21 +46,23 @@ func NewMux(opts MuxOpts, service Service) http.Handler { // Compress the assets with gzip if an accepted encoding compressed := gziphandler.GzipHandler(prefixedAssets) + // The react application handles all the routing if the server does not + // know about the route. This means that we never have unknown routes on + // the server. + hr.NotFound = compressed + + var router chronograf.Router = hr + // Set route prefix for all routes if basepath is present - var router chronograf.Router - if opts.Basepath != "" && opts.PrefixRoutes { + if opts.PrefixRoutes { router = &MountableRouter{ Prefix: opts.Basepath, Delegate: hr, } - // The react application handles all the routing if the server does not - // know about the route. This means that we never have unknown routes on - // the server. The assets handler is always unaware of basepaths, so the + + //The assets handler is always unaware of basepaths, so the // basepath needs to always be removed before sending requests to it - hr.NotFound = http.StripPrefix(opts.Basepath, compressed) - } else { - router = hr - hr.NotFound = compressed + hr.NotFound = http.StripPrefix(opts.Basepath, hr.NotFound) } /* Documentation */ From 6d218e7ac265e060391c1800e5ec14eebc6a6a4a Mon Sep 17 00:00:00 2001 From: Chris Goller Date: Tue, 4 Apr 2017 16:29:14 -0500 Subject: [PATCH 58/81] Update schema exploration to support non-default rp --- ui/src/shared/apis/metaQuery.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/src/shared/apis/metaQuery.js b/ui/src/shared/apis/metaQuery.js index 7633f69281..694041a325 100644 --- a/ui/src/shared/apis/metaQuery.js +++ b/ui/src/shared/apis/metaQuery.js @@ -36,14 +36,14 @@ export function showMeasurements(source, db) { } export function showTagKeys({source, database, retentionPolicy, measurement}) { - const query = `SHOW TAG KEYS FROM "${measurement}"` + const query = `SHOW TAG KEYS FROM "${retentionPolicy}"."${measurement}"` return proxy({source, db: database, rp: retentionPolicy, query}) } export function showTagValues({source, database, retentionPolicy, measurement, tagKeys}) { const keys = tagKeys.sort().map((k) => `"${k}"`).join(', ') - const query = `SHOW TAG VALUES FROM "${measurement}" WITH KEY IN (${keys})` + const query = `SHOW TAG VALUES FROM "${retentionPolicy}"."${measurement}" WITH KEY IN (${keys})` return proxy({source, db: database, rp: retentionPolicy, query}) } From 57d61a1d00238f94ce25840bbe94fdd4d67ed49e Mon Sep 17 00:00:00 2001 From: Chris Goller Date: Tue, 4 Apr 2017 16:33:45 -0500 Subject: [PATCH 59/81] Update CHANGELOG to mention fixing #979 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c0c12eca8b..adb0005cea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ 1. [#1164](https://github.com/influxdata/chronograf/pull/1164): Restore ability to save raw queries to a Dashboard Cell 1. [#1115](https://github.com/influxdata/chronograf/pull/1115): Fix Basepath issue where content would fail to render under certain circumstances 1. [#1173](https://github.com/influxdata/chronograf/pull/1173): Fix saving email in Kapacitor alerts + 1. [#979](https://github.com/influxdata/chronograf/issues/979): Fix empty tags for non-default retention policies ### Features 1. [#1112](https://github.com/influxdata/chronograf/pull/1112): Add ability to delete a dashboard From 25875d2d8fd38f889eeb89d705ad8795a9634cbe Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 4 Apr 2017 14:37:24 -0700 Subject: [PATCH 60/81] Pull strings into consts --- ui/src/data_explorer/components/QueryBuilder.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/ui/src/data_explorer/components/QueryBuilder.js b/ui/src/data_explorer/components/QueryBuilder.js index ac78e1458f..94540c321e 100644 --- a/ui/src/data_explorer/components/QueryBuilder.js +++ b/ui/src/data_explorer/components/QueryBuilder.js @@ -14,6 +14,9 @@ const { string, } = PropTypes +const BUILDER = 'Help me build a query' +const EDITOR = 'Type my own query' + const QueryBuilder = React.createClass({ propTypes: { queries: arrayOf(shape({})).isRequired, @@ -122,17 +125,17 @@ const QueryBuilder = React.createClass({ onChoose(item) { switch (item.text) { - case 'Help me build a query': + case BUILDER: this.handleAddQuery() break - case 'Type my own query': + case EDITOR: this.handleAddRawQuery() break } }, renderAddQuery() { - const items = [{text: 'Help me build a query'}, {text: 'Type my own query'}] + const items = [{text: BUILDER}, {text: EDITOR}] return ( From 2e0c27cd5bddd9e23cfaa377a26710e43a0e69f4 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 4 Apr 2017 14:38:07 -0700 Subject: [PATCH 61/81] Use quotes instead of backticks --- ui/src/data_explorer/components/QueryEditor.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/data_explorer/components/QueryEditor.js b/ui/src/data_explorer/components/QueryEditor.js index afd4772169..c8c81d7815 100644 --- a/ui/src/data_explorer/components/QueryEditor.js +++ b/ui/src/data_explorer/components/QueryEditor.js @@ -91,7 +91,7 @@ const QueryEditor = React.createClass({ renderQuery() { const {query, timeRange} = this.props - const statement = query.rawText || buildInfluxQLQuery(timeRange, query) || `Select a database, measurement, and field below.` + const statement = query.rawText || buildInfluxQLQuery(timeRange, query) || 'Select a database, measurement, and field below.' if (typeof query.rawText !== 'string') { return ( From 1c1c00cd022327c1ebbadf6c01e48e2c5c8ef4b2 Mon Sep 17 00:00:00 2001 From: lukevmorris Date: Tue, 4 Apr 2017 14:42:24 -0700 Subject: [PATCH 62/81] Admin Databases Page no longer breaks if a db is missing an rp (#1179) * retentionPolicies could be a blank array * Update CHANGELOG * Add `retentionPolicies` to swagger docs --- CHANGELOG.md | 1 + server/databases.go | 12 ++++++------ server/swagger.json | 18 ++++++++++++++++++ 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c0c12eca8b..9b7996186f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ 1. [#1164](https://github.com/influxdata/chronograf/pull/1164): Restore ability to save raw queries to a Dashboard Cell 1. [#1115](https://github.com/influxdata/chronograf/pull/1115): Fix Basepath issue where content would fail to render under certain circumstances 1. [#1173](https://github.com/influxdata/chronograf/pull/1173): Fix saving email in Kapacitor alerts + 1. [#1179](https://github.com/influxdata/chronograf/pull/1179): Admin Databases Page will render a database without retention policies ### Features 1. [#1112](https://github.com/influxdata/chronograf/pull/1112): Add ability to delete a dashboard diff --git a/server/databases.go b/server/databases.go index 1e04566098..805e2d3f90 100644 --- a/server/databases.go +++ b/server/databases.go @@ -16,12 +16,12 @@ type dbLinks struct { } type dbResponse struct { - Name string `json:"name"` // a unique string identifier for the database - Duration string `json:"duration,omitempty"` // the duration (when creating a default retention policy) - Replication int32 `json:"replication,omitempty"` // the replication factor (when creating a default retention policy) - ShardDuration string `json:"shardDuration,omitempty"` // the shard duration (when creating a default retention policy) - RPs []rpResponse `json:"retentionPolicies,omitempty"` // RPs are the retention policies for a database - Links dbLinks `json:"links"` // Links are URI locations related to the database + Name string `json:"name"` // a unique string identifier for the database + Duration string `json:"duration,omitempty"` // the duration (when creating a default retention policy) + Replication int32 `json:"replication,omitempty"` // the replication factor (when creating a default retention policy) + ShardDuration string `json:"shardDuration,omitempty"` // the shard duration (when creating a default retention policy) + RPs []rpResponse `json:"retentionPolicies"` // RPs are the retention policies for a database + Links dbLinks `json:"links"` // Links are URI locations related to the database } // newDBResponse creates the response for the /databases endpoint diff --git a/server/swagger.json b/server/swagger.json index 0fec463ee1..c6d96293ba 100644 --- a/server/swagger.json +++ b/server/swagger.json @@ -2259,6 +2259,18 @@ "duration": "3d", "replication": 3, "shardDuration": "3h", + "retentionPolicies": [ + { + "name": "weekly", + "duration": "7d", + "replication": 1, + "shardDuration": "7d", + "default": true, + "links": { + "self": "/chronograf/v1/ousrces/1/dbs/NOAA_water_database/rps/liquid" + } + } + ], "links": { "self": "/chronograf/v1/sources/1/dbs/NOAA_water_database", "rps": "/chronograf/v1/sources/1/dbs/NOAA_water_database/rps" @@ -2282,6 +2294,12 @@ "type": "string", "description": "the interval spanned by each shard group" }, + "retentionPolicies": { + "type": "array", + "items": { + "$ref": "#/definitions/RetentionPolicy" + } + }, "links": { "type": "object", "properties": { From 5e6578e615a5308f6efe809c5e8e29583bdcf219 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 4 Apr 2017 14:42:59 -0700 Subject: [PATCH 63/81] Add comments to elucidate various influxQL states --- ui/src/data_explorer/components/Table.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ui/src/data_explorer/components/Table.js b/ui/src/data_explorer/components/Table.js index 8b10e72603..06b853ce58 100644 --- a/ui/src/data_explorer/components/Table.js +++ b/ui/src/data_explorer/components/Table.js @@ -84,21 +84,25 @@ const ChronoTable = React.createClass({ return } + // 200 from server and no results = warn if (_.isEmpty(results)) { this.setState({cellData: emptyCells}) return onEditRawStatus(query.id, {warn: 'Your query is syntactically correct but returned no results'}) } + // 200 from chrono server but influx returns an error = warn const warn = _.get(results, 'error', false) if (warn) { this.setState({cellData: emptyCells}) return onEditRawStatus(query.id, {warn}) } + // 200 from server and results contains data = success const cellData = _.get(results, ['series', '0'], {}) onEditRawStatus(query.id, {success: 'Success!'}) this.setState({cellData}) } catch (error) { + // 400 from chrono server = fail const message = _.get(error, ['data', 'message'], error) this.setState({isLoading: false}) console.error(message) From a1d5819bb2899286ad12cd5655a62352714635b3 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 4 Apr 2017 14:46:16 -0700 Subject: [PATCH 64/81] Consolidate switch statement --- ui/src/data_explorer/components/Visualization.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ui/src/data_explorer/components/Visualization.js b/ui/src/data_explorer/components/Visualization.js index be2de88f7a..eb0ca1ed51 100644 --- a/ui/src/data_explorer/components/Visualization.js +++ b/ui/src/data_explorer/components/Visualization.js @@ -105,10 +105,9 @@ const Visualization = React.createClass({ const q = activeQuery || defaultQuery switch (view) { - case GRAPH: - return this.renderGraph(queries) case TABLE: return (q ?
:
Enter your query below
) + case GRAPH: default: this.renderGraph(queries) } From 41ef10f22719efdb4c4e8c9a9d034fbfeb4ad835 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 4 Apr 2017 14:46:55 -0700 Subject: [PATCH 65/81] Pew Pew ; --- ui/spec/data_explorer/reducers/queryConfigSpec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/spec/data_explorer/reducers/queryConfigSpec.js b/ui/spec/data_explorer/reducers/queryConfigSpec.js index 95c352c966..61c19cebf8 100644 --- a/ui/spec/data_explorer/reducers/queryConfigSpec.js +++ b/ui/spec/data_explorer/reducers/queryConfigSpec.js @@ -11,7 +11,7 @@ import { toggleTagAcceptance, updateRawQuery, editRawQueryStatus, -} from 'src/data_explorer/actions/view'; +} from 'src/data_explorer/actions/view' const fakeAddQueryAction = (panelID, queryID) => { return { From e28a37127cb1831bd57aca86420c8618ceb76909 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 4 Apr 2017 14:48:22 -0700 Subject: [PATCH 66/81] Be the change --- ui/src/data_explorer/components/Table.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/ui/src/data_explorer/components/Table.js b/ui/src/data_explorer/components/Table.js index 06b853ce58..12b676fb53 100644 --- a/ui/src/data_explorer/components/Table.js +++ b/ui/src/data_explorer/components/Table.js @@ -5,7 +5,15 @@ import fetchTimeSeries from 'shared/apis/timeSeries' import _ from 'lodash' import moment from 'moment' -const {oneOfType, number, string, shape, arrayOf, func} = PropTypes +const { + arrayOf, + func, + number, + oneOfType, + shape, + string, +} = PropTypes + const emptyCells = { columns: [], values: [], From d6e8a2180b97af06b0bafaaf0f90a80f48fa3529 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 4 Apr 2017 14:50:11 -0700 Subject: [PATCH 67/81] Remove magic number --- ui/src/data_explorer/components/Table.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ui/src/data_explorer/components/Table.js b/ui/src/data_explorer/components/Table.js index 12b676fb53..220d9be0f7 100644 --- a/ui/src/data_explorer/components/Table.js +++ b/ui/src/data_explorer/components/Table.js @@ -19,6 +19,8 @@ const emptyCells = { values: [], } +const defaultTableHeight = 1000 + const CustomCell = React.createClass({ propTypes: { data: oneOfType([number, string]).isRequired, @@ -58,7 +60,7 @@ const ChronoTable = React.createClass({ getDefaultProps() { return { - height: 1000, + height: defaultTableHeight, } }, From ff50a009179a34eaee34800a4f09c2d7e2c499f9 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 4 Apr 2017 14:55:53 -0700 Subject: [PATCH 68/81] Pull out ternary into separate function property --- ui/src/data_explorer/components/Visualization.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/ui/src/data_explorer/components/Visualization.js b/ui/src/data_explorer/components/Visualization.js index eb0ca1ed51..bbf9c90fcf 100644 --- a/ui/src/data_explorer/components/Visualization.js +++ b/ui/src/data_explorer/components/Visualization.js @@ -102,17 +102,24 @@ const Visualization = React.createClass({ renderVisualization(view, queries, heightPixels, onEditRawStatus, activeQueryIndex) { const activeQuery = queries[activeQueryIndex] const defaultQuery = queries[0] - const q = activeQuery || defaultQuery switch (view) { case TABLE: - return (q ?
:
Enter your query below
) + return this.renderTable(activeQuery || defaultQuery, heightPixels, onEditRawStatus) case GRAPH: default: this.renderGraph(queries) } }, + renderTable(query, heightPixels, onEditRawStatus) { + if (!query) { + return
Enter your query below
+ } + + return
+ }, + renderGraph(queries) { const {cellType, autoRefresh, activeQueryIndex} = this.props const isInDataExplorer = true From d56598f09522174df8ce520e4585bb50b5b1e97e Mon Sep 17 00:00:00 2001 From: Luke Morris Date: Tue, 4 Apr 2017 15:13:01 -0700 Subject: [PATCH 69/81] Resolve conflicts by taking both changes --- .gitattributes | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..a19ade077d --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +CHANGELOG.md merge=union From d278cd77b20c68cdc67ab26be9f6da967d23e992 Mon Sep 17 00:00:00 2001 From: lukevmorris Date: Tue, 4 Apr 2017 15:27:49 -0700 Subject: [PATCH 70/81] Experiment with fewer stacking contexts (#1166) --- ui/src/style/pages/dashboards.scss | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/ui/src/style/pages/dashboards.scss b/ui/src/style/pages/dashboards.scss index 40408312b2..44e56b4c09 100644 --- a/ui/src/style/pages/dashboards.scss +++ b/ui/src/style/pages/dashboards.scss @@ -38,10 +38,6 @@ $dash-graph-heading: 30px; border-radius: $radius; border: 2px solid $g3-castle; transition-property: left, top, border-color, background-color; - - &:hover { - z-index: 8000; - } } .graph-empty { background-color: transparent; @@ -58,10 +54,8 @@ $dash-graph-heading: 30px; height: 100%; top: 0; left: 0; - z-index: 0; } .dash-graph--container { - z-index: 1; user-select: none !important; -o-user-select: none !important; -moz-user-select: none !important; @@ -73,7 +67,7 @@ $dash-graph-heading: 30px; top: $dash-graph-heading; left: 0; padding: 0; - + & > div:not(.graph-empty) { position: absolute; left: 0; @@ -93,7 +87,7 @@ $dash-graph-heading: 30px; } } .dash-graph--heading { - z-index: 2; + z-index: 1; user-select: none !important; -o-user-select: none !important; -moz-user-select: none !important; @@ -500,4 +494,4 @@ $overlay-bg: rgba($c-pool, 0.7); .overlay-technology .dash-graph--container { height: calc(100% - #{$dash-graph-heading}); top: $dash-graph-heading; -} \ No newline at end of file +} From 249d56c16ea5d4a300e1bf601844ddbb59936f29 Mon Sep 17 00:00:00 2001 From: Chris Goller Date: Tue, 4 Apr 2017 17:42:30 -0500 Subject: [PATCH 71/81] Add checks for null retention policies in meta queries --- ui/src/shared/apis/metaQuery.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ui/src/shared/apis/metaQuery.js b/ui/src/shared/apis/metaQuery.js index 694041a325..fb8424a4f6 100644 --- a/ui/src/shared/apis/metaQuery.js +++ b/ui/src/shared/apis/metaQuery.js @@ -1,4 +1,5 @@ import AJAX from 'utils/ajax' +import _ from 'lodash' import {buildInfluxUrl, proxy} from 'utils/queryUrlGenerator' export const showDatabases = async (source) => { @@ -36,14 +37,15 @@ export function showMeasurements(source, db) { } export function showTagKeys({source, database, retentionPolicy, measurement}) { - const query = `SHOW TAG KEYS FROM "${retentionPolicy}"."${measurement}"` - + const rp = _.toString(retentionPolicy) + const query = `SHOW TAG KEYS FROM "${rp}"."${measurement}"` return proxy({source, db: database, rp: retentionPolicy, query}) } export function showTagValues({source, database, retentionPolicy, measurement, tagKeys}) { const keys = tagKeys.sort().map((k) => `"${k}"`).join(', ') - const query = `SHOW TAG VALUES FROM "${retentionPolicy}"."${measurement}" WITH KEY IN (${keys})` + const rp = _.toString(retentionPolicy) + const query = `SHOW TAG VALUES FROM "${rp}"."${measurement}" WITH KEY IN (${keys})` return proxy({source, db: database, rp: retentionPolicy, query}) } From a08e36ec24c9118a3581bf9a4a69bbecb42ef3c9 Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Tue, 4 Apr 2017 17:11:48 -0700 Subject: [PATCH 72/81] Remove switch statement --- ui/src/data_explorer/components/Visualization.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/ui/src/data_explorer/components/Visualization.js b/ui/src/data_explorer/components/Visualization.js index bbf9c90fcf..353b4e9aa8 100644 --- a/ui/src/data_explorer/components/Visualization.js +++ b/ui/src/data_explorer/components/Visualization.js @@ -103,13 +103,11 @@ const Visualization = React.createClass({ const activeQuery = queries[activeQueryIndex] const defaultQuery = queries[0] - switch (view) { - case TABLE: - return this.renderTable(activeQuery || defaultQuery, heightPixels, onEditRawStatus) - case GRAPH: - default: - this.renderGraph(queries) + if (view === TABLE) { + return this.renderTable(activeQuery || defaultQuery, heightPixels, onEditRawStatus) } + + return this.renderGraph(queries) }, renderTable(query, heightPixels, onEditRawStatus) { From 9f4cd64c117bba850c83db5a6a19cc5ccb6fed85 Mon Sep 17 00:00:00 2001 From: Alex P Date: Tue, 4 Apr 2017 17:41:42 -0700 Subject: [PATCH 73/81] Remove custom cursors --- ui/assets/images/cursor-default.cur | Bin 1014 -> 0 bytes ui/assets/images/cursor-default.png | Bin 1392 -> 0 bytes ui/assets/images/cursor-ew-resize.cur | Bin 1162 -> 0 bytes ui/assets/images/cursor-ew-resize.png | Bin 1366 -> 0 bytes ui/assets/images/cursor-invert.cur | Bin 2162 -> 0 bytes ui/assets/images/cursor-invert.png | Bin 1964 -> 0 bytes ui/assets/images/cursor-move.cur | Bin 3086 -> 0 bytes ui/assets/images/cursor-move.png | Bin 1828 -> 0 bytes ui/assets/images/cursor-nesw-resize.cur | Bin 1430 -> 0 bytes ui/assets/images/cursor-nesw-resize.png | Bin 1489 -> 0 bytes ui/assets/images/cursor-ns-resize.cur | Bin 1214 -> 0 bytes ui/assets/images/cursor-ns-resize.png | Bin 1407 -> 0 bytes ui/assets/images/cursor-nwse-resize.cur | Bin 1430 -> 0 bytes ui/assets/images/cursor-nwse-resize.png | Bin 1452 -> 0 bytes ui/assets/images/cursor-pointer.cur | Bin 1574 -> 0 bytes ui/assets/images/cursor-pointer.png | Bin 1488 -> 0 bytes ui/assets/images/cursor-text.cur | Bin 1022 -> 0 bytes ui/assets/images/cursor-text.png | Bin 1414 -> 0 bytes ui/src/style/chronograf.scss | 1 - .../style/components/custom-time-range.scss | 8 ++--- ui/src/style/components/dygraphs.scss | 2 +- ui/src/style/components/input-tag-list.scss | 2 +- ui/src/style/components/react-tooltips.scss | 4 +-- ui/src/style/components/resizer.scss | 2 +- ui/src/style/components/tables.scss | 4 +-- .../style/external/fixed-data-table-base.scss | 4 +-- ui/src/style/external/fixed-data-table.scss | 4 +-- ui/src/style/layout/page.scss | 4 +-- ui/src/style/layout/sidebar.scss | 6 ++-- ui/src/style/modules/custom-cursors.scss | 34 ------------------ ui/src/style/pages/dashboards.scss | 16 ++++----- .../pages/data-explorer/query-builder.scss | 2 +- .../pages/data-explorer/query-editor.scss | 4 +-- .../pages/data-explorer/visualization.scss | 2 +- ui/src/style/pages/kapacitor.scss | 2 +- ui/src/style/theme/bootstrap-theme.scss | 18 +++++----- ui/src/style/theme/bootstrap.scss | 26 +++++++------- ui/src/style/theme/theme-dark.scss | 12 +++---- 38 files changed, 61 insertions(+), 96 deletions(-) delete mode 100644 ui/assets/images/cursor-default.cur delete mode 100644 ui/assets/images/cursor-default.png delete mode 100644 ui/assets/images/cursor-ew-resize.cur delete mode 100644 ui/assets/images/cursor-ew-resize.png delete mode 100644 ui/assets/images/cursor-invert.cur delete mode 100644 ui/assets/images/cursor-invert.png delete mode 100644 ui/assets/images/cursor-move.cur delete mode 100644 ui/assets/images/cursor-move.png delete mode 100644 ui/assets/images/cursor-nesw-resize.cur delete mode 100644 ui/assets/images/cursor-nesw-resize.png delete mode 100644 ui/assets/images/cursor-ns-resize.cur delete mode 100644 ui/assets/images/cursor-ns-resize.png delete mode 100644 ui/assets/images/cursor-nwse-resize.cur delete mode 100644 ui/assets/images/cursor-nwse-resize.png delete mode 100644 ui/assets/images/cursor-pointer.cur delete mode 100644 ui/assets/images/cursor-pointer.png delete mode 100644 ui/assets/images/cursor-text.cur delete mode 100644 ui/assets/images/cursor-text.png delete mode 100644 ui/src/style/modules/custom-cursors.scss diff --git a/ui/assets/images/cursor-default.cur b/ui/assets/images/cursor-default.cur deleted file mode 100644 index 4316cf736569df31fbe1d8bc819ba0b883e79b1c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1014 zcma)*YexK6Tv9zR|K~jKIse~z&WHD9Oh8ZK zaz?w5jU_Oa!Wfg05=kNw&+~PdYAcq86-v-{e4V%@dgjq65~b-gM31%UnKPO+;kD{i zAyIReC0~M*8zg5;O0j@f8su452KMj54@Wa>s$%F?Wvb^AjJ0i&;c7)8S~g1U<~&J? zC0{oEy{7@_HN#P+!V&T6o$<`MEpi0z8PHu-h}L4+XuoDV;68@0T}GIclBvd=ti|!n z#gc4bSdYL%9b9{g;OjjI+^@$SV;N{}YrLPoJImJsuMPn(wD{@O0$u}V9_evmhXOSB zx6?&ZKh5oL&Xf30=xUK9ILi{OYB0Z$DA4|FtlE$$vI;d75z z3#94%p1!?G;6o$uz7BY2Aif58L;NVk=Q?<-r8vJ;{;Xs3nq=IrIjZBAZvaE>Row6Tmp^*XptYR)yGVb0!_nTck$l|W`M>_iJ$CYk{)5>~y7+XeSQB6nOizud&3L-Vi3rGp1g`^}>21!Nw4g3yCh5!Hn diff --git a/ui/assets/images/cursor-default.png b/ui/assets/images/cursor-default.png deleted file mode 100644 index eb3e522ea0c0b908dbb6a764ac3ccb84fe9e4d75..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1392 zcmeAS@N?(olHy`uVBq!ia0vp@KrG0?1|-@3tIYvYk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+m^Cs(B1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxOgGuU%v{0TQqR!T z+}y-mN5ROz&{W^RSl`${*T~q)#K6kLNC66zfVLH-q*(>IxIyg#@@$ndN=gc>^!3Zj z%k|2Q_413-^$jg8E%gnI^o@*kfhu&1EAvVcD|GXUm0>2hq!uR^WfqiV=I1GZOiWD5 zFD$Tv3bSNU;+l1ennz|zM-B0$V)JVzP|XC=H|jx7ncO3BHWAB;NpiyW)Z+ZoqGVvir744~DzI`cN=+=uFAB-e&w+(vKt_H^esM;Afr7I$IMft0d=ry1 z^FV@{U|qhxR-SpqC5d^-sh%#jNVg zEu36kfTC_Ny)OC5rManjB{01y2)%|l^@0*aZUN9{m(-%nveXo}qWoM1u*a=3vAD(2 z(9FQl*&Jw|nK^d1K=h_yaSK$h5l+4OK*#8Vq8BN;VM4$(1Y*JyFOUOI{;7GuG+zWv z-0NdfS{N7@?|Hg7hE&{|a^bYMbf5_PgZncyUL4z^7IZO-Da)wLAh|YSsdVcjW>sy? zUBO=3Z{-8B7kZVyFLH!VciQ@5Mv9toadleFLQOk1 zzVBzA);+&d{ozY&!Gxy6w)<4N__`FEru|m1D9&A%tE!I=3;gT+VYj{!4YuC(eRTGxbX%`8skR zXPTE8Ef7@iX@2op$^Xo{YssH-K6=+zF(u7vOMWXd>&Kdg)f-tNpRVI-I;h^OEEzsg z$N7@JwPa-oYYFRv*QXw?d8GZ~j7E+RPsZ@n^ z;lueKey#d=Aklr^gJaMB1pN)oemMq%(-?e^Q`MwvMK9yxZeY{U=?ho^2 z;s<1pE|l|J;JESi2?>|{ioLr(*EI8$u4P-^5zRY zrsVEc;iF1rDy8xzU6Q76#!a|oE<@Itbc(3|RC>>8YrOYdo_1Pi&n2#ujLim1)9q)c z_IlAtOZe}E&p!AF|E;9u#O|<8)JA-H-(N*8(^u}<7e6`E&U`4aDK#$ctP#q}Nek!X z-o6{~k&Ly%bkyc&_gCcWfvN)isJc);ttrwkE_w7weX$;I@aoA%pPp*=Yw-hLhS+=) zW2y7Jd6!#~X{JulIgYZH6AG;uV6V`j-(*yV%-{$jmia#3(7Q*GG#^PT4-|~<6LHrDSr z1<%~X^wgl##FWaylc_cg49pstArU1JzCKpT`MG+DAT@dwxdlMo3=B5*6$OdO*{LN8 zNvY|XdA3ULckfqH$V{%1*XSQL?vFu&J;D8jzb> zlBiITo0C^;Rbi_HHrEQs1_|pcDS(xfWZNo192Makpx~Tel&WB=XQrEMU}mmhZmDNz zYHn^~uA^XNU}&muV61O!plf7oWny4uVx#~CNh+i#(Mch>H3D2mX`VkM*2oZxQ#zd*s+860W~8or6i znRy^VO|ULsUn|eN;*!L?5Y=8i^2ZqCMT z<`zz_E=C3hZZN$r`N^fZsd*(Zy(tL2jyUy#5=3qR&}Ns^qRg_?6t|-MTm`Vltuk@D z#T=)3P`xR*-C}`LuRhQ*`k?4Vif)(?Fb#p2@Wcz`z>|M!9x%-p0TcHO5!Ocx42(xS zT^vIyZY3ogU<%=#RdL1VR@lnuGHwF_MjqqxH72L-9c}jQI6(Vn;sUVllkwA6%@#!D7ITzlW~$T&Rz_xt1Q#J8)wdCK4IyUl81 zFO+hS(a+BA_mfR&ZS`}`@YLUsJRsJPsql|`X=0LsvxUS$6C-KQp0D@o+ott3^W5L} z->%~_`)hTd76N-J_k3X%{P|#ibOYbveYxg#fB$~g z=gauuHbX{vTMYwG*Vh9cSC}rjL?1YDq(t-t50g|KcfQ;1;;oWP4#;$bPoCfv&bX0v f*FyycfdqyP`*yzeowdFfRML66`njxgN@xNA#QE?r diff --git a/ui/assets/images/cursor-invert.cur b/ui/assets/images/cursor-invert.cur deleted file mode 100644 index 933c1c217dc71ab3a6ccdf9a51291e04d9ba703d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2162 zcma);3s6*59LDc1k7Z$jU0fCxc3}lp9+EEvbu46jvZRrc_{wb5GDi(l)5Hm6kQ5;m zjJL>us3;i^A1DYc@70tg#%AxR!C59zzW9rAWd_#l_;l?_17w!n_q6Ndqa zb8%1f7vly~m?=X+mK^!J*)KA{M3a$3JV)eqB!e>^Sm}uJH`ORxXT<(@hNGkU3QD%dp=eto3U@3=MSd1+ z&A;J74`6M)jr_bgI>Lz;5$`?NSGCv)hn9Mwaz!XAR!zdyE}*mOI!d=}W#$~T*Zh>T zel>huT+vfe4AdSu!TQC}V*&1F@Zh7JDF{=#VM@?2Jh)^9%F;_wl)eT<+hb9%GYt8=bgZ8XGmEt$$Eipa zPD~6$UCdO}PEW#(c1(5iLY&?oSL#c#IAScux@b_5vYzu$aSLS|ideri?0a_sO47!& ze*P%PR3krI!TQP37^=dV5oVl179)U zm*flbx%E$cdioxY<;1amo+#d?muA%2)k?`(Q!F|)>Mv%GUyLV5J7-NeYy$m!r#B zg-$m!+C402(D#W7IeBMqqhZb>x1!@w;5}^g0NB-h}Y?E$xQ*iuDp+? zY1ypbeALH`XZ^S?W@=D9TY;mAa#Ssna86PYGi-7TdK@+jU#f2yw)li%6TKUJg7HcD z4&b}ud$5jr4^5%-aC+o;G(?ziVuBj=(F)W~k)y^U;aL1*-#ICk-i^YSX?zA5hH#|& zkHE!)JAs>f@1iRp16H3zwEB(1*#Hxog4JjYSD<0E94E&~IR31Jy2+1xfpkqHNy4|+ ze>C<4jKS5KT)MJ;g-x>+U8=e0P)DJCh(D~}YSvGI=Am-&JBr3IiJ2v?W#MxnL&+qv zT=)uyMd6#{dBBZ~U+}Rq1?OZ5u-QkVTj7UpXLr_5few#8SD&xNPeOB`gr*STb0$U- zMG}PX2P?m8H`4Fmyh9SN%^_on5jL4S>*vUM{@c~(Yu8G!>Lspa;d3HhWH^ZtK3q?| ziSvquJfA@#hhsy` zB(6D$Yfkv=iI_tV;zI)Z5q=#JE(anfqW_bz$;Ca{C}>;u7TNw_n!>B|5Rzh_!4B)MA?dV-xpB5z)7oe;9}z RQ4n|HPbQJnUa1Ye(qA~3E^Yt- diff --git a/ui/assets/images/cursor-invert.png b/ui/assets/images/cursor-invert.png deleted file mode 100644 index 9810a9184fe6a06cc858fd93a14e9d6e2a4f5f03..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1964 zcmeAS@N?(olHy`uVBq!ia0vp^B0wz3!3HD^#rZCSI3=zTCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+m^Cs(B1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxOgGuU%v{0TQqR!T z+}y-mN5ROz&{W^RSl`${*T~q)#K6kLNC66zfVLH-q*(>IxIyg#@@$ndN=gc>^!3Zj z%k|2Q_413-^$jg8E%gnI^o@*kfhu&1EAvVcD|GXUm0>2hq!uR^WfqiV=I1GZOiWD5 zFD$Tv3bSNU;+l1ennz|zM-B0$V)JVzP|XC=H|jx7ncO3BHWAB;NpiyW)Z+ZoqGVvir744~DzI`cN=+=uFAB-e&w+(vKt_H^esM;Afr7I$IMft0d=ry1 z^FV@{U|qhxR-SpqC5d^-sh%#jNVg zEu36kj0_CiV0vBhlS^|`^GaZPQxJMxaOwpmh};68%`T}$nPsUdZbkXI3Sf_0W#V>= zBTn<6dQ)(_#R;cgeV}9XLD7p8-7q0w8Uiuli5JL$C;!wuV45!iChpS?*FG>Xut<5j zIEGZ*nljZtBPLYj*#7VJ_hwg4HvD$pVunxCk*>876CZiq=`0Nwe6{S66N`DM;X%DF zv#YILc_NuXJfYfAM;wm0iiL&5Tv5{6c4f&Gg=-T=HdSf4|ZwELZS}uLt@*?+FPM#GxcM3CJZ^#Rl_p$jd`m6r;?t%p^<~72%e>mQp zreU54(>m?6-d_FLOY9cEj5e!y_BIT0~zQdw!!b zA#B#dwo>NMlf3Ggj)KA~w%m$2cIAfp3fcdvoAVj;wKW`_7dlK_$CW5-r^t7A<{w_K zwDOaZ(=M`c@ro{LJDl8JnE1>(e@jo`yXCT$ti1s$3nugn1n@my#&!G4qjj5(zm)8J z{A<=}F_U!HpFsf{WslI^Flt715r`!y3Sc^4hD_RQ$F-~GU+e9QB19;ow0}p(3ey_B- zF#G9|OdtDCH~d%@w%4DVfAqcm--aT4w;kI5zOt+8t$TRkq1C&OP|~p=Zd;7e>oP=XIstHFn*7So*yDRK0M{ z4D%Ge&-{H4x{W_eJhnM5^Yk<0ww#^P)y(qmf>^ts|2uHmv+47n&!VsFdEdnNEOuYw zyP&CR%REID@0A-TC=nDy6jAUiD9UOhrZkHmiN@4cqfx|$ zswf)K5?f+W{J?06nkXey1Qj)c>w~V=(TvzKrJcnG*91JCJ7#sV%q(k6GMt_JzxUkp zW7*vyiZYB>Mux)IY$ZNiQAR3?l0&hjlu*)_zjDsL?33LXz{{G<(JIwNX#-eKd!;IS zj9Q&N#%P>ciVdTECeK5{tMU8PO@5zoBfJu5+JmmD3e=8OP4)+sw>ei;f5=sho9kBq z#~VKAUhQi9IL>c!J}A7cnqQ@i>+6=_b@Mh`IJFD$=IwTUYvXd%X?~NQ&*Ie=`ZK8r zWjO@2?m)xjJg^sY^rNhOX@B@>7z2VE?oL zSi7>=&+^)L_V-p99I_m5lVILCD#Y0~S_GvMz={82RttX)~`dp=JSjCAfc>z^jFCVR5d$w~Gw*GEMu z%OSvQNw8v$euQ;qa>l2exvzCp7Mag4)}q&Fv4wxnp!dc5T0g%*-k(ABd+vP-!|4^? zm2k27vU4Z!U)u+Dw{Jm5<@iD4B$yw0KLP)Dpu4?(NI1#7+dnTFQjU|y_t-U%&j&H+ z_SLynN8MB%N|yds`9g;Uz1Qm1Us-STZY!=|v*MvTrSG5hK5)Gi&z8Q-#V3B*v-g=} K=Si+yENgU diff --git a/ui/assets/images/cursor-move.png b/ui/assets/images/cursor-move.png deleted file mode 100644 index d1677bbcdefa47805bafe61c996f8737b070c5dc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1828 zcmaJ?c~BEq91aChQW;TDi02xrMI^~)lMoY$l2aorw*>^nkZfSZWJ9un0Af+`0-9J_ z(0TwmwlY}7q9V`=Dk@q)C`wVHf;isRV~S(Bq#G*QKT3CI_r3R<@B4n|o81`_w9J`2 zolGK;oaM_UVZ=Ja_D*mlzF*zwT}dnyTpERkV`@BAp+`tU6&8;Ga*ZMZ2}2aBwB)OZ zABkk|g+@f-QE(t%iD~Ex8-{Mu=m<86G7Z;pssU%Jd{9Peon21n@KQ2>}g)D*%&bz1F}t38*8wd}41iGpN7_1WyuB z{|PDz4gtiN9sxLXUz!s14beGw-CH4(>kdW z>Tnd(0yafOJZ8iNR3g%UQqbr=$Z8Gm%S056VN&QAOgd;wX%q;<{}0t@KA;VF81gaR z{}eVvr0Ebw7-GPTdL_{~wWrOMjxW|D3LMi%VA%T6DuyItIA%!1bbweqQZ>K}Rwz-e z&A4EM1;c!~)_^OtN<=OZPzfD6imLcbj!?oBLu^pQ^<^@pTp#4H(ikw_|mKu|Wy zm0(Jv2GQc9T-8S|XH2ebAT&C{vjowjDTqp@$27o5;(T=MSfDZW-g8xB$HE$u%OHYb z*xLKAc8{8f8M19Z3@>r`F#d>^n0Y-h+|)IHT8TT8FP8`-Or56+G|SzD9u38U4#@it zg!cM4Ti|+@{xYxR98l>db*T68+C|^fO=p#OZbmuqguo^QTHgUST46ONeA_;>H~ z+t&42+g(ew$0WF}aL&>3^_#Rl6&nZaLz9Z&U6K(&K(?f^J8VlR$ke&W0JzvduV&Xw4SWie6=$Sg{tg^o`+wUY1OWxtW4&u zSg&pcojxafx`Obm%EdXFnf*?WlZqW0t?BLLoSt77rtWp`r1^_F!A|!gO5rMgXHaIV zMI2{0RPUeC_b|6=Sw&-Ia=~|vF?kI<$uQr^y7M6vZ^;?h`eyPCbX{pNJTJE4dtW=F zpR#;WSJ5feruyh`hv9{%i?yE&ThF`uyptBh^?Pkd?`)vq>a09ghQ7D#MK{M76Jl^a zErgq&+83Ogf3f}?i<`G%&M&4&?^|Z8RT}doy7t+I+_mH>bY8;b>?M}#S4xZ>vM5z{ zZQhna&K`5uy9MmozRRawribZMS_!V9nl? zU34BgW*p?#EzZm2amoB^~j!%w*A)N80x(3FY^rAl0$}GG%gm@Pe1*V zg2!U74|N5%&e>fPbJO#XlcnciDL52x$C5_tm)G2T`lddUO73`a{^sKoVZC3+lP?M^ zZ|f|+tX)%K5A$1(jqw%xjfGb-)O{TO;O&#Uo(r2TjlsKXtIiCS43y7q0b4ijs6BVQ z;8||dt-ii{mFRU>z->Zz``qqnQd66$W4oy~(vSKy?UHnh^Q0vnMcle>b7a1EzPec+ z2*dWSwLY&Wep+-qDDha`1JAMx0SAon?DDFp3(Y6Wjiwj3+f)6pHIYsGnTwgorg4WO z86|zcPn~ml<=s1mz`RS_nN#jVZAr;;%I(4X09(hV04BS27qMX&@;anErlksGB lTX*!E%HhL_s^_V8j--whM}D~W{%PBPOD+wP92c$K{5Obq(bfO} diff --git a/ui/assets/images/cursor-nesw-resize.cur b/ui/assets/images/cursor-nesw-resize.cur deleted file mode 100644 index c4fe030c1798cc3336822e7abf3cee363a5bd5a5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1430 zcmbu7Pe@cz6vodW(ZWSCi#CBa>e!?D$%Kkf z6yZQIkD)><`a`;qhYP`iRxyhTF>qJuHs%d}-tXy^L7nLhy~nxdz4Pw4iGTu)cL+KGK30SNz7*)w*4**_#c!v}CjB zg*wQve)MDE?rFTf9x&eC2yC;+P=V}qdp*+&b&!!+ir`bM4Iet{jhW7dA~F`EXW)cG zHoZ^>8JV9kd>KE_wJ9>JmAGRLS@c33WMozz;(M~)X5jbpf;ePtc}bChUkQ+bKhp(s zbU)}tGqC!uAWkj@)@B@Y$WUR=&y^);iog`tR`6Nws0f-8C^Kc?fGL^%VOduvhKB@U Nd%9p*Wrn#~+W;>Jb}|3} diff --git a/ui/assets/images/cursor-nesw-resize.png b/ui/assets/images/cursor-nesw-resize.png deleted file mode 100644 index 653fd5ce9392829759285c11e61f98136cd714d3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1489 zcmaJ>eM}Q~7(N(lfE0DOC{+{B;}2nx-t~^Qa8hY$DKxPR3kci%x?X>U6WTj_$0)cd z&TObCLyUY#*wCrTGGvM~BO+3yag(nsj4&n61*dakE=-*nL2P$L!2L0H$=&ZG@AJIR z`@T=^U}i>IMEHtu2!bL^=?06qMh4zciTK?-Sd}X-G9fij$YSk63FQKi&c<2+Y;sWf zzyc^+>8=|f34&gUVY2gtJo849W*u@W5F>XxI1vp&Ny%=Gq6>ilTR}eK)F2ZbT?owB zG)S(}jF~w-C}7gdTp+6~BbzQOq!VpO@&-7`O^O5#AW*Q|QRL)Fw+5NvCB=Q<8b#ol z5TQ_mybzUV&V==>3&2XbDvrifYFM2pS18r$c=b9M#}rBwBTxd1!wE7TBja&+_CdsK zE}NaS7>u*I#FGXo5Co1y(UOu9c?ltBUHPaYF)-95TgJaAT%{T*> zu`?*MnKU_hfpXHoWY8cYkDOs_q|&G|DpiC5OHr#7id3}@$BlZzs7^>pHQ*R#oWmMe zy4V4n!W`E25}UdpHed(`CuTMP7xON#8C|Rco++GU7S@uoAm1$3wy+lcf>=}(g9gU? zuW`>^i5?1U=glh~=IsxhqUT+rxr^$Sk3vvzvdN&!c8^+ZaBviu7>6wPCx^H2AUEnl*~ zUANdJx@_e{M@4a;TGih|cY_NqK`19fQN}#>a{)FiCZAk8PFaFXP zwYHkwPfT6v%qIw(KqE)84h&UZ1npO+6MDXAL~9jo*f(3Zygt1=y6N+VuZo*jp1O}8 zf2;5P>z0bPt^SLx(>`|l#fdKGw>4)nS?jACJ@qkZs*OFZt5>bRJKnZw>SUGlhh=}{ zyLbQAjW_4rK6Cu&-d!?p@Zksv#W2;BRGXi>-zXykNdny7ySyO6;p*3a42mb(L CH#LX= diff --git a/ui/assets/images/cursor-ns-resize.cur b/ui/assets/images/cursor-ns-resize.cur deleted file mode 100644 index ffdcd04c1fde7652a3a6ef2beca1f9210a82eef4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1214 zcmaizPe>GD7{))bn;t|#yhLm2x~;lSn~91@=)n$xdZ?fTI|w2QORNy(A3|LX3E zEI}74H^D#;x+}JL6FdkicykTgC^B22ON39)E9)@3y3;rO=6&Ahedc3e=2I$(S4x)R z-J;%KP%2X?Wg{s>BjU@laF$L}NVHrRvolDLq$kRV-4L2kpd3Jt%fdx6=(lPW#^dSkv%%d>h)Y)9d77qHN$BR&0y%q2fq zVofF68$(S%fP2OvKIdDK8!pfH5g5VhV~kc8#(1317~e(o^C}y#_7G#WMW9ZLpBwv^ zHCKQQHzw*Fpf=6Ex@iY?T$pThfZ8HVeu-}ilmTRCk&CWF!1L0vTv|b0bX?DIv1HQh%Vn->cW3r2FSJUDE zzD;2HK^f>@GS2+%P9MRFyIcp~z5L8K^j_vMc*Zv zbN{E#O35vegl99F&tN7<&Yby3NU2bi;C-c@Sd_kgg_QaqdYD3DSr z1<%~X^wgl##FWaylc_cg49pstArU1JzCKpT`MG+DAT@dwxdlMo3=B5*6$OdO*{LN8 zNvY|XdA3ULckfqH$V{%1*XSQL?vFu&J;D8jzb> zlBiITo0C^;Rbi_HHrEQs1_|pcDS(xfWZNo192Makpx~Tel&WB=XQrEMU}mmhZmDNz zYHn^~uA^XNU}&muV61O!plf7oWny4uVx#~CNh+i#(Mch>H3D2mX`VkM*2oZxQ#zd*s+860W~8or6i znRy^VO|ULsUn|eN;*!L?5Y=8i^2ZqCMT z<`zz_E=C3hZZN$r`N^fZsd*(Zy(tL2W;peN5=3qR&}Ns^qRg_?6t|-MTm`VltunE= z#n9Bz#n9Qv5vO@jy(zfeVuDkzKF~4xpy)-4ZkP}-4S|^O#0%uWlYeR+FwGYM6L(qM zEL{c$#jv*Ddl2RO)-EQt~X}l7pz-MKC^{wnC8R1z@X$o8CbWgr2=i>bHhA6Y+ zchKa|)u-~NBYPy70Ym!UfU|Nr%7Ho8<-^P4sJx7?E+`TO;aBAN_8 zHqfiOy}3!{q^(l-scJG2Mjv+MgFr);b&UOoIbl;|J#01fi3&~{WLTJ`rIE7#7NW_S4AQg4ZS zHr2PPzPr6HnIYwt@?BGNT7yac^9N6g{(k&>TTkKk>wACa?T-*`ZdjCKw?|{g{|Y6q cKn8&XhWC1GYq@8ueE=1Sp00i_>zopr0OFMhWB>pF diff --git a/ui/assets/images/cursor-nwse-resize.cur b/ui/assets/images/cursor-nwse-resize.cur deleted file mode 100644 index 03abaaa5af405f8ee6706e2316341bc2c1295cb2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1430 zcmbu7O-NL66vh9;pJ>sdm_?hQnsjVZn?`A&2$>-$DNzvwEutV23=Kh4Bxwd=QYKV{ zB8diyc?=cyK_8?G5f?!Ptzs4xV&KlSt@wsc_xzm~!^?4=G{box_uc#Boco?qKKc9o zN{;(gbemGUl~N&*lvJsRb^dnAGyjftnSzJIaLqwq{Ak(GU{w&&a1a9(rRWcppf^~8 z?!W4SV-Tl3t5wwPGIf%0LXGOa$jCJcN)v}c*7yiAtn#!#=ox+&w23ho#%V>7b_qZA7;h9REJXL8;_Z)})={m@wFJ^S+xO;vR&b|Ab zJ|EVpbBC>&8}+ciw1X`AVn%l_S<~05hurnai%0a63t?+6)(HD+2gsr?W_0&D_rgIw z>(!NresMWsy}#8A`+G0QqAzB2W;QyL^C5oHu%_Cp_1m^8>tmu7+4&gAqAzCLXg0q_ zj=h+4Seegbr;+`U09o|KjQRbAuF1jA$AcFS;p`0bfpLSZaR&){J%X diff --git a/ui/assets/images/cursor-nwse-resize.png b/ui/assets/images/cursor-nwse-resize.png deleted file mode 100644 index 662b16dced118bbd222c8948c236c2c1255e0432..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1452 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+1|-AI^@Rf|$r9IylHmNblJdl&R0hYC{G?O` z&)mfH)S%SFl*+=BsWuD@%o>>?5hW46K32*3xq68pHF_1f1wh>l3^w)^1&PVosU-?Y zsp*+{wo31J?^jaDOtDo8H}y5}EpSfF$n>ZxN)4{^3rViZPPR-@vbR&PsjvbXkegbP zs8ErclUHn2VXFi-*9yo63F|8hm3bwJ6}oxF$}kgLQj3#|G7CyF^YauyCMG83 zmzLNn0bL65LT&-v*t}wBFaZNhzap_f-%!s0*lEl2^R8JRMC7?NanVBh8j;0po#%^vF22SQ?hK8=@jz&gq&c<%$ z7EZ1%Mg|6MFugAM$)&lec_lEtDG0rmIQ4=OL~a4lW|!2W%(B!Jx1#)91+d4hGI6`b z45xWey(zfeVvbXYE% znepTlfiu?WeRuZn%RZ~h_S(UgEmL%&2HS$fUxnE-JIy%6)EwTwu-o%5>E^-E=Mp~6 zy%H){jx753|L1NG{TfT-K(>QS-|m=v-|wJ*)OP2$oSPq~E|>V?>??WXis~YVdsjGP zPWYTqm~vyM!Ey1N8=Fu2Pn#QCHOJBT(j-R@XRbHCx$kdU@FpKoW&Ff{N#q&d5p4>6pSGnV}{TYD;txqfd{(W_$~FP z_5}(~E4!zm^YDtEj$+`yw};u;nl^s?_3hdJMP2ix9rYL`ljL5U{P6Op_FaGdxOOI9 z&JP_ONjoZDC@=?RKKZ&g_rK#k#+@uF9b5P7Ge4eRzsO83jz{Lq8=myS*{R04+bnVj}P&=*~+Hwkx_;l8t8DnioktY5gC-llI?n10xd;gN2Q`DCbGN Q383=R)78&qol`;+02vA$Bme*a diff --git a/ui/assets/images/cursor-pointer.cur b/ui/assets/images/cursor-pointer.cur deleted file mode 100644 index bc164824e87a7b2f79ecbdf0657f1157a265dfbb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1574 zcmc)JZ%7ky7zgm*h$5&Lr%>z5EbhWk%h(5ksGq8OgUc|8AKTt z6hSXcsa~vb)RxrhRlUiS$gnrH$Q4#z=k84i@$Gx(Hm-4w?Nu9}yXW`2=lgKGZ9>@i zE?OXXTq>kFLM#+QIH-t9NZ;3A-Lr)#o;S_dusHY^Et3d=#ksNPrA6G>3a(f#wK*L? zUuRC&wp4??o2=%vI9BevvAr4yb>uYIQU~_-WY3UG?ek@qJrTd&2t?X*n(A-G`O1>t z%oLmFbd;~U@UVUx@cdj}9yRO$Gye39i&d-bS2jDRv?qSx)#?j8qoqv;u!YaG$n zmv#Ze%>`i&`i$40tnZa}V>lx4%?X2hU*g z%~|w&Hz0Jh0!Z*TN?)Jx*`#XozPV-|p~Ef@DfE+?!rn^Y+f~C%b*RpT$)R=wGvCwS zt$q&Imq}6IdSLvP!CZ%rl_B}I)4)EBl$5>Z8POAN>1m(}_|a>KHn^dUbRqe!3%sV0 zlHxJv%=JK{TZ%RF|2FZ!z%@uEg=WlI&iEAtNS|T8o=?pT?_(pN#ti>)hHb!Cu4o_n zkbK{xf0prl?mxSSIS);w?9RS!wN`DiP{p)38+0#HWS*^Fac;(E$s)dl@Gq6?ziy$- A-2eap diff --git a/ui/assets/images/cursor-pointer.png b/ui/assets/images/cursor-pointer.png deleted file mode 100644 index 691a3ec4a8e0610c15e2a4ddfcdb3c9b89ca9ae9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1488 zcmeAS@N?(olHy`uVBq!ia0vp^f|6H_V+Po~;1FfeOmhD4M^`1)8S=jZArg4F0$^BXQ!4Z zB&DWj=GiK}-@RW+Av48RDcsc8z_-9TH6zobswg$M$}c3jDm&RSMakYy!KT6rXh3di zNuokUZcbjYRfVk**jy_h8zii+qySb@l5ML5aa4qFfP!;=QL2Keo|$g4ftk62xuu?= zskym{xsHO7fuX6sfw8`^fv%CUm5G6siID;nC;@FNN=dT{a&d#&1?1T(Wt5Z@Sn2DR zmzV368|&p4rRy77T3YHG80i}s=>k>g7FXt#Bv$C=6)VF`a7isrF3Kz@$;{7F0GXJW zlwVq6s|0i@#0$9vaAWg|p}_Q4xWV+g7>0hA=QN z*?GD+hE&{2N_1d$ySe*N;}sJP^S5_Gwr}NOVGy3h*wfR)bJ_coPAk`D-IfVFhq)sz z{P-jDMyE;Wk&VGV3Bxu6!vMwwyIUUG3;h4(zfe)(!(VwXi&};tg^4~13hPyJPVBGe z`QP|M`pks?|0N!6Ui(w>-~aRoi?26i7N3#5ywGUo^JUd*j%1zKe}BK^KSSl1LtH$D zd4e7a|9^j;t!{K(?o6EB%Lg*eQ_dPNUyl6p_rR#xg6Gm$ zrOs)d;x1IUa9}}AxNE7UmCXS>WeeDg$B!!OAh4Qjp? z`@fmoxY^@m!((&6fMMfB!}+^J=B$-ZWle96;VAOF{@J+vo&DZ#B04LkKkvLMkaA|N zVy^+qlS!*@U#|P}`|Iil&mJ}N_&5YUo6nPF z%$Mca7JXd1_P9VL`-?SqqG$L2`S<%1H}mUqwnZPFOP_c6m1lIAr}B5ocWcMQ=y&tq z@4Im#GgIJc*X!H-#(V0&tPxceXZFpltoU_k^Y`oWMjQSm9O#y`|DRS-^6QcRf_bhQ zZFlxv`SA4r`TIwoo4-j(V6+mKudDfFba?&w_;BX0dk&vC`oHe~t&i_s2D43*|FQZ1 z+Q;e+S?P^8dR|QG+QiD_@XD51S-CFw-+%kM{~uYj1%wq2Fnrsbe)3-W>S$0I>*?y} Jvd$@?2>^eAK*az6 diff --git a/ui/assets/images/cursor-text.cur b/ui/assets/images/cursor-text.cur deleted file mode 100644 index 51d8adf631e665d942c087948b85ece1cd030c3e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1022 zcma))J4hT+6o!waTmwT`0@`TsEo#=?;HwbXCV_xrp$H;~fe;^{8xa&#L{V5RMy<9; zEPQ~t79xa*FGR4^%9MgDB7<0nAf!-!&$-OiJBl%P_~t+7KR79)I>rpWSsB0&TX8Wlgr(Zg;@yf$uV<1Q zEXY)h1adJ~6UJ3+2#5XOluADw$hptxvy6taUo+#2^6o*>@H-#+Si!pfHrc;?Bjwgm9I?Sle|Zfr!#R4qQ^$*#2q zEf?UVADfXnu(5&i4q1o=|m=%Jkv<_@wx-aB4;uH diff --git a/ui/assets/images/cursor-text.png b/ui/assets/images/cursor-text.png deleted file mode 100644 index 0469da82c1959430e8e9191992e169c6c647a655..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1414 zcmeAS@N?(olHy`uVBq!ia0vp^+(0bC!3HFwb&h8MDajJoh?3y^w370~qErUQl>DSr z1<%~X^wgl##FWaylc_cg49pstArU1JzCKpT`MG+DAT@dwxdlMo3=B5*6$OdO*{LN8 zNvY|XdA3ULckfqH$V{%1*XSQL?vFu&J;D8jzb> zlBiITo0C^;Rbi_HHrEQs1_|pcDS(xfWZNo192Makpx~Tel&WB=XQrEMU}mmhZmDNz zYHn^~uA^XNU}&muV61O!plf7oWny4uVx#~CNh+i#(Mch>H3D2mX`VkM*2oZxQ#zd*s+860W~8or6i znRy^VO|ULsUn|eN;*!L?5Y=8i^2ZqCMT z<`zz_E=C3hZZN$r`N^fZsd*(Zy(tL2mN@l-5=3qR&}Ns^qRg_?6t|-MTm`Vltuk@D z#SEu;P`xR*-C~YYuRhQ*`k?4Vif)(?Fb#p2@Wcz`z>|M!9x%-p0TXww5%)|62F5R* zE{-7;w~`D`-TOL!;zUNCfB(}fIG0RgE)*A-&RD1t;3uPX`~Xu@$@U#hqWdd<`_`Oq z{LPwv&i1sa;iROp!hjS3!DmdTr-r;wOJJGF^Jv2c149i>PD2)!3`3imAB~Q2aSe%w z+p>ASzuTMqeV1+3D4bB}@N2<1{e%SAYH&bJFFhzMef1qRw~Ee&E+Q`-%C`u}q^gcmFT!a?IKD zkf+4_bp3+cj^`3s*yipEH8&4CnssdLp8xg#w1hDz>J$G_)G z`n|t%u;B5jo%K>yZa!sZ^LYO3JCv|fbM920o~}==4}LUN2~JzPt8s_Ol(h{-j{+>& c8W@-vwmMv#Te9xyO;E|`>FVdQ&MBb@0EYPl>;M1& diff --git a/ui/src/style/chronograf.scss b/ui/src/style/chronograf.scss index dcf9abed7a..3b8593688b 100644 --- a/ui/src/style/chronograf.scss +++ b/ui/src/style/chronograf.scss @@ -1,7 +1,6 @@ // Modules @import 'modules/influx-colors'; @import 'modules/variables'; -@import 'modules/custom-cursors'; // Mixins @import 'mixins/mixins'; diff --git a/ui/src/style/components/custom-time-range.scss b/ui/src/style/components/custom-time-range.scss index ab701d4257..4a761ef8f0 100644 --- a/ui/src/style/components/custom-time-range.scss +++ b/ui/src/style/components/custom-time-range.scss @@ -150,13 +150,13 @@ $rd-cell-size: 30px; border-radius: 5px; &:hover { - cursor: $cc-pointer; + cursor: pointer; color: $g20-white !important; background-color: $g6-smoke; } &.rd-day-next-month, &.rd-day-prev-month { - cursor: $cc-default; + cursor: default; color: $g8-storm !important; background-color: $g5-pepper !important; } @@ -196,7 +196,7 @@ $rd-cell-size: 30px; &:hover { color: $g20-white; background-color: $g6-smoke; - cursor: $cc-pointer; + cursor: pointer; } } .rd-time-list { @@ -230,7 +230,7 @@ $rd-cell-size: 30px; &:active, &:focus { color: $g20-white; - cursor: $cc-pointer; + cursor: pointer; outline: none; @include gradient-h($c-laser, $c-pool); } diff --git a/ui/src/style/components/dygraphs.scss b/ui/src/style/components/dygraphs.scss index a2ed1ab34e..028094b96b 100644 --- a/ui/src/style/components/dygraphs.scss +++ b/ui/src/style/components/dygraphs.scss @@ -2,7 +2,7 @@ .dygraph { &:hover { - cursor: $cc-invert; + cursor: default; } } diff --git a/ui/src/style/components/input-tag-list.scss b/ui/src/style/components/input-tag-list.scss index f7d6f23808..43cd070915 100644 --- a/ui/src/style/components/input-tag-list.scss +++ b/ui/src/style/components/input-tag-list.scss @@ -27,6 +27,6 @@ $input-tag-item-height: 24px; &:hover { color: $c-dreamsicle; - cursor: $cc-pointer; + cursor: pointer; } } diff --git a/ui/src/style/components/react-tooltips.scss b/ui/src/style/components/react-tooltips.scss index fceb2e13ef..df2227227b 100644 --- a/ui/src/style/components/react-tooltips.scss +++ b/ui/src/style/components/react-tooltips.scss @@ -31,7 +31,7 @@ $tooltip-code-color: $c-potassium; border: 2px solid $tooltip-accent !important; border-radius: $tooltip-radius !important; text-transform: none !important; - cursor: $cc-default; + cursor: default; p { margin: 0; @@ -125,7 +125,7 @@ $qmark-tooltip-size: 15px; background-color 0.25s ease; } .question-mark-tooltip:hover { - cursor: $cc-default; + cursor: default; .question-mark-tooltip--icon { background-color: $c-pool; } diff --git a/ui/src/style/components/resizer.scss b/ui/src/style/components/resizer.scss index 3d60e4ab15..9de7e84706 100644 --- a/ui/src/style/components/resizer.scss +++ b/ui/src/style/components/resizer.scss @@ -59,7 +59,7 @@ $resizer-color-active: $c-pool; background-color 0.19s ease; } &:hover { - cursor: $cc-resize-ns; + cursor: ns-resize; &:before { background-color: $resizer-color-hover; diff --git a/ui/src/style/components/tables.scss b/ui/src/style/components/tables.scss index 14f1d68b6d..2d99e41abe 100644 --- a/ui/src/style/components/tables.scss +++ b/ui/src/style/components/tables.scss @@ -72,7 +72,7 @@ table .monotype { position: absolute; top: 50%; right: 8px; - color: #fff; + color: $g20-white; font-family: 'icomoon'; opacity: 0; transform: translateY(-50%); @@ -83,7 +83,7 @@ table .monotype { } &:hover { - cursor: $cc-pointer; + cursor: pointer; color: $g19-ghost; background-color: $g5-pepper; diff --git a/ui/src/style/external/fixed-data-table-base.scss b/ui/src/style/external/fixed-data-table-base.scss index eecda97013..f00badda94 100644 --- a/ui/src/style/external/fixed-data-table-base.scss +++ b/ui/src/style/external/fixed-data-table-base.scss @@ -95,7 +95,7 @@ } .fixedDataTableCellLayout_columnResizerContainer:hover { - cursor: $cc-resize-ew; + cursor: ew-resize; } .fixedDataTableCellLayout_columnResizerContainer:hover .fixedDataTableCellLayout_columnResizerKnob { @@ -120,7 +120,7 @@ */ .fixedDataTableColumnResizerLineLayout_mouseArea { - cursor: $cc-resize-ew; + cursor: ew-resize; position: absolute; right: -5px; width: 12px; diff --git a/ui/src/style/external/fixed-data-table.scss b/ui/src/style/external/fixed-data-table.scss index 967fa46de2..cc66d901a1 100644 --- a/ui/src/style/external/fixed-data-table.scss +++ b/ui/src/style/external/fixed-data-table.scss @@ -95,7 +95,7 @@ } .fixedDataTableCellLayout_columnResizerContainer:hover { - cursor: $cc-resize-ew; + cursor: ew-resize; } .fixedDataTableCellLayout_columnResizerContainer:hover .fixedDataTableCellLayout_columnResizerKnob { @@ -120,7 +120,7 @@ */ .fixedDataTableColumnResizerLineLayout_mouseArea { - cursor: $cc-resize-ew; + cursor: ew-resize; position: absolute; right: -5px; width: 12px; diff --git a/ui/src/style/layout/page.scss b/ui/src/style/layout/page.scss index ec70465b82..0034b3e96a 100644 --- a/ui/src/style/layout/page.scss +++ b/ui/src/style/layout/page.scss @@ -244,7 +244,7 @@ background-color 0.25s ease; &:hover { - cursor: $cc-pointer; + cursor: pointer; background-color: $g18-cloud; color: $g9-mountain; } @@ -390,7 +390,7 @@ $form-static-checkbox-size: 16px; transform 0.25s cubic-bezier(0.175, 0.885, 0.32, 1.275); } &:hover { - cursor: $cc-pointer; + cursor: pointer; color: $g20-white; &:before { diff --git a/ui/src/style/layout/sidebar.scss b/ui/src/style/layout/sidebar.scss index d63cfcd0bf..e93b9d85a5 100644 --- a/ui/src/style/layout/sidebar.scss +++ b/ui/src/style/layout/sidebar.scss @@ -49,7 +49,7 @@ $sidebar-logo-color: $g8-storm; &:hover { background-color: $g9-mountain; - cursor: $cc-pointer; + cursor: pointer; } } @@ -121,7 +121,7 @@ $sidebar-logo-color: $g8-storm; } &:hover { - cursor: $cc-pointer; + cursor: pointer; .sidebar__icon { color: $sidebar-icon-hover; @@ -270,7 +270,7 @@ $sidebar-logo-color: $g8-storm; transform: translate(-50%,-50%); } &:hover { - cursor: $cc-pointer; + cursor: pointer; background-color: $sidebar-item-hover; color: $sidebar-icon-hover; diff --git a/ui/src/style/modules/custom-cursors.scss b/ui/src/style/modules/custom-cursors.scss deleted file mode 100644 index 789479197d..0000000000 --- a/ui/src/style/modules/custom-cursors.scss +++ /dev/null @@ -1,34 +0,0 @@ -/* - Custom Mouse Cursors - ---------------------------------------------- -*/ - -$cc-default: url(assets/images/cursor-default.cur), auto !important; -$cc-invert: url(assets/images/cursor-invert.cur) 4 4, auto !important; -$cc-pointer: url(assets/images/cursor-pointer.cur) 6 0, auto !important; -$cc-text: url(assets/images/cursor-text.cur) 6 10, auto !important; -$cc-move: url(assets/images/cursor-move.cur) 13 13, auto !important; -$cc-resize-ns: url(assets/images/cursor-ns-resize.cur) 6 12, auto !important; -$cc-resize-ew: url(assets/images/cursor-ew-resize.cur) 12 5, auto !important; -$cc-resize-nwse: url(assets/images/cursor-nwse-resize.cur) 9 9, auto !important; -$cc-resize-nesw: url(assets/images/cursor-nesw-resize.cur) 9 9, auto !important; - - -// Resetting defaults -body { - cursor: $cc-default; -} -a:link, -a:visited, -a:hover, -a:active { - cursor: $cc-pointer; -} - -input[type="text"], -input[type="password"], -textarea, -code, -pre { - cursor: $cc-text; -} \ No newline at end of file diff --git a/ui/src/style/pages/dashboards.scss b/ui/src/style/pages/dashboards.scss index 40408312b2..0ab4468838 100644 --- a/ui/src/style/pages/dashboards.scss +++ b/ui/src/style/pages/dashboards.scss @@ -117,7 +117,7 @@ $dash-graph-heading: 30px; color 0.25s ease, background-color 0.25s ease; &:hover { - cursor: $cc-default; + cursor: default; } } .dash-graph--drag-handle { @@ -137,7 +137,7 @@ $dash-graph-heading: 30px; transition: opacity 0.25s ease; } &:hover { - cursor: $cc-move; + cursor: move; } &:hover:before { opacity: 1; @@ -184,7 +184,7 @@ $dash-graph-heading: 30px; } &:hover { - cursor: $cc-text; + cursor: text; color: $g20-white; &:after { @@ -273,7 +273,7 @@ $dash-graph-options-arrow: 8px; } &:hover { - cursor: $cc-pointer; + cursor: pointer; background-color: $g6-smoke; color: $g20-white; } @@ -352,20 +352,20 @@ $dash-graph-options-arrow: 8px; border-image-outset: 0; border-image-width: 2px; border-image-source: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyhpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMDY3IDc5LjE1Nzc0NywgMjAxNS8wMy8zMC0yMzo0MDo0MiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTUgKE1hY2ludG9zaCkiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MTg0NjVDRkVGMEVFMTFFNkE0QjVFRTJGNEI1ODc0RDMiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MTg0NjVDRkZGMEVFMTFFNkE0QjVFRTJGNEI1ODc0RDMiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDoxODQ2NUNGQ0YwRUUxMUU2QTRCNUVFMkY0QjU4NzREMyIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDoxODQ2NUNGREYwRUUxMUU2QTRCNUVFMkY0QjU4NzREMyIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PpeetfIAAAMnSURBVHja7N1PatwwFMfxJ5NlKT1DIfQKWZfSA/Q0hexDL9McoOQAPUKglwhp6dZ9Ho/HfyTZs6l+b/E1GDm27IH5oH9Pyji9//7XfLtNZt88/eT722TzlvrFseXHaXFmypuO8vd5nmW6nyeNefrKfZv7i9f75blU/NzafXvns2dV7tl8zqsnT55+9f3Xjf/xwQ9+evou+xLB+N8Ydi4AX3z/6PnvOj94AEOGMV/rB4P00J2rKTC0GNOTPne0GWEwhv1NB0YYjNPWgREHI00gYMTAOIGAEQdjuKcDIw7GXGWBEQJjrLLACIORrFBlgaHDsG2VBYYWY1VlgaHHSH3WqIOhxLB1ow6GGmPRqIMRAeMMAkYUDFuGTsDQYwxP6MCIg1Hp9oKhwih0e8FQYthuLAuM5hj1WBYYEoxUjGWBIcOwrFEHQ4qxLiFgyDFOvSww4mCM8yFghMEoDgzB0GGk2owhGBoMq5UQMDQYxRIChg4ja0PA0GLYMrgIhh7jUkLAiIExV1lghMA4GBiC0RrjNIULRhyMysAQDBVGYWAIhhJjM6cOhhpjUULAiIAxr1wEIwTGPDAEIwTGWGWBEQajHu0FQ4JRjvaCIcPIo71gSDHW0V4w5Bj5SB0MKUZxoRwYOoxsPgQMLcZqPgQMPUaxUQdDh2HVcQgYEoxUHIeAIcPIqywwpBjrKgsMOcb8f+pghMDIwu9gaDFWI3Uw9Bg2N+pgRMA497LAiIJRXf0OhgajuPodDB3G1dFeMNpgXBXtBaMdxmG0F4y2GLvRXjDaY2wGhmCoMawU7QVDh5G20V4wtBjzwBCMEBiXVx6BEQPjsJcFRluM3V4WGO0xqr0sMDQYVuplgaHDWL1YEgw9hi17WWDoMVJ1ChcMCYYVp3DBkGFUl5KCocGw6deAwIiBYUfBRTDaYmTdXjC0GFYLLoKhwSj+cAAYOgzbBhfB0GKsgotg6DGuWrkIRjuMudsLRgiMsQ0BIwzG5ZVHYMTAmKqsVzBiYPj2Z+j2PoERAmM4/2MoIfe+v4Ahx3jx5H4AefYLd37q0Y9/g9EcY/jOHz11A3v+J8AA9wisahRCWTQAAAAASUVORK5CYII=); - cursor: $cc-move; + cursor: move; &:hover { - cursor: $cc-move; + cursor: move; } .dash-graph--drag-handle:before, .dash-graph--drag-handle:hover:before { opacity: 1 !important; - cursor: $cc-move; + cursor: move; } } & > .react-resizable-handle { background-image: none; - cursor: $cc-resize-nwse; + cursor: nwse-resize; &:before, &:after { diff --git a/ui/src/style/pages/data-explorer/query-builder.scss b/ui/src/style/pages/data-explorer/query-builder.scss index 79b9e32231..ab9681b5dc 100644 --- a/ui/src/style/pages/data-explorer/query-builder.scss +++ b/ui/src/style/pages/data-explorer/query-builder.scss @@ -44,7 +44,7 @@ color: $g11-sidewalk; background: transparent; height: 30px; - cursor: $cc-pointer; + cursor: pointer; padding: 0 8px 0 16px; transition: color 0.25s ease, diff --git a/ui/src/style/pages/data-explorer/query-editor.scss b/ui/src/style/pages/data-explorer/query-editor.scss index 39f5830761..8673cfc5cc 100644 --- a/ui/src/style/pages/data-explorer/query-editor.scss +++ b/ui/src/style/pages/data-explorer/query-editor.scss @@ -43,7 +43,7 @@ background-color 0.25s ease; &:hover { - cursor: $cc-pointer; + cursor: pointer; color: $g20-white; } &.active { @@ -74,7 +74,7 @@ &:hover { background-color: $g5-pepper; color: $g15-platinum; - cursor: $cc-pointer; + cursor: pointer; } } &-radio { diff --git a/ui/src/style/pages/data-explorer/visualization.scss b/ui/src/style/pages/data-explorer/visualization.scss index 232a4caf59..64defa13dc 100644 --- a/ui/src/style/pages/data-explorer/visualization.scss +++ b/ui/src/style/pages/data-explorer/visualization.scss @@ -184,7 +184,7 @@ line-height: 30px; margin-right: 2px; border-radius: 4px 4px 0 0; - cursor: $cc-pointer; + cursor: pointer; padding: 0 10px; transition: color 0.25s ease, diff --git a/ui/src/style/pages/kapacitor.scss b/ui/src/style/pages/kapacitor.scss index 590347e4f8..1e85c98700 100644 --- a/ui/src/style/pages/kapacitor.scss +++ b/ui/src/style/pages/kapacitor.scss @@ -467,7 +467,7 @@ div.qeditor.kapacitor-metric-selector { &:hover { color: $c-rainforest; - cursor: $cc-pointer; + cursor: pointer; } } } diff --git a/ui/src/style/theme/bootstrap-theme.scss b/ui/src/style/theme/bootstrap-theme.scss index cfafe87c7d..529877637c 100755 --- a/ui/src/style/theme/bootstrap-theme.scss +++ b/ui/src/style/theme/bootstrap-theme.scss @@ -515,7 +515,7 @@ a:active.link-warning { } .btn:hover, .btn:focus { - cursor: $cc-pointer; + cursor: pointer; } .btn-group-xs > .btn, .btn.btn-xs { @@ -1475,7 +1475,7 @@ fieldset[disabled] .btn-link-success:active:focus { transition: background-color .25s ease, color .25s ease, border-color .25s ease; } .panel-available:hover { - cursor: $cc-pointer; + cursor: pointer; background-color: #f0fcff; border-color: #bef0ff; } @@ -2595,7 +2595,7 @@ a.badge:hover, a.badge:focus { color: #00c9ff; text-decoration: none; - cursor: $cc-pointer; + cursor: pointer; } .sparkline { display: inline-block; @@ -3201,7 +3201,7 @@ a.badge:focus { border-radius: 4px; } .slider-plan-picker:hover { - cursor: $cc-pointer; + cursor: pointer; } .slider-plan-picker .slider-label, .slider-plan-picker .slider-cell { @@ -3873,7 +3873,7 @@ table.table.icon-font-matrix tr > td strong { -ms-flex-direction: column; } .docs-color-swatch:hover { - cursor: $cc-pointer; + cursor: pointer; } .docs-color-swatch.light-bg { color: #676978; @@ -4148,7 +4148,7 @@ section.docs-section .page-header { } .nav-tablist > li > a:hover { color: #676978; - cursor: $cc-pointer; + cursor: pointer; background-color: #f6f6f8; } .nav-tablist > li:first-of-type > a { @@ -4254,7 +4254,7 @@ section.docs-section .page-header { transform: translate(-50%, -50%) rotate(-45deg); } .microtabs-dismiss:hover { - cursor: $cc-pointer; + cursor: pointer; } .microtabs-dismiss:hover:after, .microtabs-dismiss:hover:before { @@ -4293,7 +4293,7 @@ section.docs-section .page-header { } .nav-microtabs > li > a:hover { color: #676978; - cursor: $cc-pointer; + cursor: pointer; background-color: #fafafc; } .nav-microtabs > li:last-of-type > a { @@ -4503,7 +4503,7 @@ section.docs-section .page-header { .nav-microtabs-summer .microtabs-dismiss:hover, .nav-microtabs-fall .microtabs-dismiss:hover, .nav-microtabs-winter .microtabs-dismiss:hover { - cursor: $cc-pointer; + cursor: pointer; } .nav-microtabs-spring .microtabs-dismiss:hover:after, .nav-microtabs-summer .microtabs-dismiss:hover:after, diff --git a/ui/src/style/theme/bootstrap.scss b/ui/src/style/theme/bootstrap.scss index b3cff6aad7..bffeab149f 100644 --- a/ui/src/style/theme/bootstrap.scss +++ b/ui/src/style/theme/bootstrap.scss @@ -155,12 +155,12 @@ html input[type="button"], input[type="reset"], input[type="submit"] { -webkit-appearance: button; - cursor: $cc-pointer; + cursor: pointer; } button[disabled], html input[disabled] { - cursor: $cc-default; + cursor: default; } button::-moz-focus-inner, @@ -1504,7 +1504,7 @@ hr { } [role="button"] { - cursor: $cc-pointer; + cursor: pointer; } h1, @@ -3331,7 +3331,7 @@ input[type="search"] { padding-left: 20px; margin-bottom: 0; font-weight: normal; - cursor: $cc-pointer; + cursor: pointer; } .radio input[type="radio"], @@ -3356,7 +3356,7 @@ input[type="search"] { margin-bottom: 0; font-weight: normal; vertical-align: middle; - cursor: $cc-pointer; + cursor: pointer; } .radio-inline + .radio-inline, @@ -3764,7 +3764,7 @@ select[multiple].input-lg { vertical-align: middle; -ms-touch-action: manipulation; touch-action: manipulation; - cursor: $cc-pointer; + cursor: pointer; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; @@ -4959,7 +4959,7 @@ select[multiple].input-group-sm > .input-group-btn > .btn { .nav-tabs > li.active > a:hover, .nav-tabs > li.active > a:focus { color: #575e6b; - cursor: $cc-default; + cursor: default; background-color: #fafbfc; border: 1px solid #ddd; border-bottom-color: transparent; @@ -5830,7 +5830,7 @@ fieldset[disabled] .navbar-inverse .btn-link:focus { .pagination > .active > span:focus { z-index: 3; color: #fff; - cursor: $cc-default; + cursor: default; background-color: #22adf6; border-color: #22adf6; } @@ -5947,7 +5947,7 @@ a.label:hover, a.label:focus { color: #fff; text-decoration: none; - cursor: $cc-pointer; + cursor: pointer; } .label:empty { @@ -6047,7 +6047,7 @@ a.badge:hover, a.badge:focus { color: #fff; text-decoration: none; - cursor: $cc-pointer; + cursor: pointer; } .list-group-item.active > .badge, @@ -6998,7 +6998,7 @@ button.list-group-item-danger.active:focus { .close:focus { color: #000; text-decoration: none; - cursor: $cc-pointer; + cursor: pointer; filter: alpha(opacity = 50); opacity: .5; } @@ -7006,7 +7006,7 @@ button.list-group-item-danger.active:focus { button.close { -webkit-appearance: none; padding: 0; - cursor: $cc-pointer; + cursor: pointer; background: transparent; border: 0; } @@ -7633,7 +7633,7 @@ button.close { height: 10px; margin: 1px; text-indent: -999px; - cursor: $cc-pointer; + cursor: pointer; background-color: #000 \9; background-color: rgba(0, 0, 0, 0); border: 1px solid #fff; diff --git a/ui/src/style/theme/theme-dark.scss b/ui/src/style/theme/theme-dark.scss index 210e98c344..4c20d0347c 100644 --- a/ui/src/style/theme/theme-dark.scss +++ b/ui/src/style/theme/theme-dark.scss @@ -293,7 +293,7 @@ input { color 0.25s ease; &:hover { - cursor: $cc-pointer; + cursor: pointer; background-color: transparent; color: $g20-white !important; } @@ -479,7 +479,7 @@ code { &:after { transform: translate(-50%,-50%) rotate(-45deg); } &:hover { - cursor: $cc-pointer; + cursor: pointer; &:before, &:after { @@ -528,7 +528,7 @@ $toggle-border: 2px; color 0.25s; &:hover { - cursor: $cc-pointer; + cursor: pointer; color: $g14-chromium; background-color: $g4-onyx; } @@ -616,7 +616,7 @@ $form-static-checkbox-size: 16px; transform 0.25s cubic-bezier(0.175, 0.885, 0.32, 1.275); } &:hover { - cursor: $cc-pointer; + cursor: pointer; color: $g20-white; &:before { @@ -693,7 +693,7 @@ $form-static-checkbox-size: 16px; transform 0.25s cubic-bezier(0.175, 0.885, 0.32, 1.275); } &:hover { - cursor: $cc-pointer; + cursor: pointer; color: $g20-white; &:before { @@ -731,7 +731,7 @@ $form-static-checkbox-size: 16px; transition: background-color 0.25s ease; } label:hover { - cursor: $cc-pointer; + cursor: pointer; background-color: $g2-kevlar; } label:after { From 12d4360cda6ed8644d7ac853c07746ea7d578fd8 Mon Sep 17 00:00:00 2001 From: Luke Morris Date: Tue, 4 Apr 2017 20:43:15 -0700 Subject: [PATCH 74/81] Missed one --- ui/src/style/unsorted.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/src/style/unsorted.scss b/ui/src/style/unsorted.scss index a06dbe0d62..473831de78 100644 --- a/ui/src/style/unsorted.scss +++ b/ui/src/style/unsorted.scss @@ -71,7 +71,7 @@ } .form-group label, .form-group label:hover { - cursor: $cc-default; + cursor: default; } /* @@ -89,4 +89,4 @@ .icon { margin-bottom: 11px; } -} \ No newline at end of file +} From 74c9a6974abdacfbfc26111392a3ae860fd2c7fc Mon Sep 17 00:00:00 2001 From: lukevmorris Date: Tue, 4 Apr 2017 20:43:47 -0700 Subject: [PATCH 75/81] Admin Databases Page should be sorted alphabetically (#1185) * Sort DBs by name * Sort RPs alphabetically * Update CHANGELOG --- CHANGELOG.md | 1 + ui/src/admin/components/DatabaseManager.js | 6 ++++-- ui/src/admin/components/DatabaseTable.js | 5 ++++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 96a2fc54c0..8f13669c1b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ 1. [#1137](https://github.com/influxdata/chronograf/pull/1137): Clarify Kapacitor Alert configuration for HipChat 1. [#1079](https://github.com/influxdata/chronograf/issues/1079): Remove series highlighting in line graphs 1. [#1124](https://github.com/influxdata/chronograf/pull/1124): Polished dashboard cell drag interaction, use Hover-To-Reveal UI pattern in all tables, Source Indicator & Graph Tips are no longer misleading, and aesthetic improvements to the DB Management page + 1. [#1185](https://github.com/influxdata/chronograf/pull/1185): Alphabetically sort Admin Database Page ## v1.2.0-beta7 [2017-03-28] ### Bug Fixes diff --git a/ui/src/admin/components/DatabaseManager.js b/ui/src/admin/components/DatabaseManager.js index e0272b7ff3..961d6b5f42 100644 --- a/ui/src/admin/components/DatabaseManager.js +++ b/ui/src/admin/components/DatabaseManager.js @@ -1,4 +1,7 @@ import React, {PropTypes} from 'react' + +import _ from 'lodash' + import DatabaseTable from 'src/admin/components/DatabaseTable' const DatabaseManager = ({ @@ -31,7 +34,7 @@ const DatabaseManager = ({
{ - databases.map(db => + _.sortBy(databases, ({name}) => name.toLowerCase()).map(db =>
{ - database.retentionPolicies.map(rp => { + _.sortBy(database.retentionPolicies, ({name}) => name.toLowerCase()).map(rp => { return ( Date: Wed, 5 Apr 2017 10:03:28 -0600 Subject: [PATCH 76/81] Don't close cell headers when editing. --- ui/src/shared/components/NameableGraph.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ui/src/shared/components/NameableGraph.js b/ui/src/shared/components/NameableGraph.js index a6e13fdaae..8a1f06f0de 100644 --- a/ui/src/shared/components/NameableGraph.js +++ b/ui/src/shared/components/NameableGraph.js @@ -80,6 +80,9 @@ const NameableGraph = React.createClass({ if (evt.key === 'Enter') { onUpdateCell(cell)() } + if (evt.key === 'Escape') { + onEditCell(x, y, false)() + } }} /> ) @@ -88,7 +91,7 @@ const NameableGraph = React.createClass({ } let onClickHandler - if (isEditable) { + if (!isEditing && isEditable) { onClickHandler = onEditCell } else { onClickHandler = () => { From 8dbe4bea5385b22a59cfc41bd947228022e4e3c5 Mon Sep 17 00:00:00 2001 From: Hunter Trujillo Date: Wed, 5 Apr 2017 10:30:44 -0600 Subject: [PATCH 77/81] Blur the cell header edit field when Esc is pressed. --- ui/src/shared/components/NameableGraph.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/shared/components/NameableGraph.js b/ui/src/shared/components/NameableGraph.js index 8a1f06f0de..f56960f94d 100644 --- a/ui/src/shared/components/NameableGraph.js +++ b/ui/src/shared/components/NameableGraph.js @@ -81,7 +81,7 @@ const NameableGraph = React.createClass({ onUpdateCell(cell)() } if (evt.key === 'Escape') { - onEditCell(x, y, false)() + onEditCell(x, y, true)() } }} /> From 278ee726f476b27934ec0155a0ecf9aafb5d9fe8 Mon Sep 17 00:00:00 2001 From: Hunter Trujillo Date: Wed, 5 Apr 2017 10:40:14 -0600 Subject: [PATCH 78/81] Changelog, #1189. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f13669c1b..dfa164ea29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ 1. [#1173](https://github.com/influxdata/chronograf/pull/1173): Fix saving email in Kapacitor alerts 1. [#979](https://github.com/influxdata/chronograf/issues/979): Fix empty tags for non-default retention policies 1. [#1179](https://github.com/influxdata/chronograf/pull/1179): Admin Databases Page will render a database without retention policies + 1. [#1189](https://github.com/influxdata/chronograf/pull/1189): Clicking inside the graph header edit box will no longer blur the field. Use the Escape key for that behavior instead. ### Features 1. [#1112](https://github.com/influxdata/chronograf/pull/1112): Add ability to delete a dashboard From 765cb6e5ded78393d401a67a4d103f513768e011 Mon Sep 17 00:00:00 2001 From: lukevmorris Date: Wed, 5 Apr 2017 13:34:17 -0700 Subject: [PATCH 79/81] Replace Kill Query confirmation Modal with ConfirmButtons (#1187) * Extract QueryRow; replace modal with ConfirmButtons * Untabify * Update CHANGELOG --- CHANGELOG.md | 1 + ui/src/admin/components/QueriesTable.js | 36 ++------------- ui/src/admin/components/QueryRow.js | 59 +++++++++++++++++++++++++ ui/src/admin/containers/QueriesPage.js | 20 ++------- ui/src/style/pages/admin.scss | 21 +++++---- 5 files changed, 80 insertions(+), 57 deletions(-) create mode 100644 ui/src/admin/components/QueryRow.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f13669c1b..4fcb85d852 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ 1. [#1137](https://github.com/influxdata/chronograf/pull/1137): Clarify Kapacitor Alert configuration for HipChat 1. [#1079](https://github.com/influxdata/chronograf/issues/1079): Remove series highlighting in line graphs 1. [#1124](https://github.com/influxdata/chronograf/pull/1124): Polished dashboard cell drag interaction, use Hover-To-Reveal UI pattern in all tables, Source Indicator & Graph Tips are no longer misleading, and aesthetic improvements to the DB Management page + 1. [#1187](https://github.com/influxdata/chronograf/pull/1187): Replace Kill Query confirmation modal with ConfirmButtons 1. [#1185](https://github.com/influxdata/chronograf/pull/1185): Alphabetically sort Admin Database Page ## v1.2.0-beta7 [2017-03-28] diff --git a/ui/src/admin/components/QueriesTable.js b/ui/src/admin/components/QueriesTable.js index 9d02e5d28c..224d195444 100644 --- a/ui/src/admin/components/QueriesTable.js +++ b/ui/src/admin/components/QueriesTable.js @@ -1,6 +1,8 @@ import React, {PropTypes} from 'react' -const QueriesTable = ({queries, onKillQuery, onConfirm}) => ( +import QueryRow from 'src/admin/components/QueryRow' + +const QueriesTable = ({queries, onKillQuery}) => (
@@ -14,41 +16,11 @@ const QueriesTable = ({queries, onKillQuery, onConfirm}) => (
- {queries.map((q) => { - return ( - - - - - - - ) - })} + {queries.map((q) => )}
{q.database}{q.query}{q.duration} - -
- - ) diff --git a/ui/src/admin/components/QueryRow.js b/ui/src/admin/components/QueryRow.js new file mode 100644 index 0000000000..371c5165ef --- /dev/null +++ b/ui/src/admin/components/QueryRow.js @@ -0,0 +1,59 @@ +import React, {PropTypes, Component} from 'react' + +import ConfirmButtons from 'src/shared/components/ConfirmButtons' + +class QueryRow extends Component { + constructor(props) { + super(props) + + this.handleInitiateKill = ::this.handleInitiateKill + this.handleFinishHim = ::this.handleFinishHim + this.handleShowMercy = ::this.handleShowMercy + + this.state = { + confirmingKill: false, + } + } + + handleInitiateKill() { + this.setState({confirmingKill: true}) + } + + handleFinishHim() { + this.props.onKill(this.props.query.id) + } + + handleShowMercy() { + this.setState({confirmingKill: false}) + } + + render() { + const {query: {database, query, duration}} = this.props + + return ( + + {database} + {query} + {duration} + + { this.state.confirmingKill ? + : + + } + + + ) + } +} + +const { + func, + shape, +} = PropTypes + +QueryRow.propTypes = { + query: shape().isRequired, + onKill: func.isRequired, +} + +export default QueryRow diff --git a/ui/src/admin/containers/QueriesPage.js b/ui/src/admin/containers/QueriesPage.js index a3a517fca3..6b6e3e5989 100644 --- a/ui/src/admin/containers/QueriesPage.js +++ b/ui/src/admin/containers/QueriesPage.js @@ -26,7 +26,6 @@ class QueriesPage extends Component { constructor(props) { super(props) this.updateQueries = ::this.updateQueries - this.handleConfirmKillQuery = ::this.handleConfirmKillQuery this.handleKillQuery = ::this.handleKillQuery } @@ -44,7 +43,7 @@ class QueriesPage extends Component { const {queries} = this.props return ( - + ) } @@ -84,20 +83,9 @@ class QueriesPage extends Component { }) } - handleKillQuery(e) { - e.stopPropagation() - const id = e.target.dataset.queryId - - this.props.setQueryToKill(id) - } - - handleConfirmKillQuery() { - const {queryIDToKill, source, killQuery} = this.props - if (queryIDToKill === null) { - return - } - - killQuery(source.links.proxy, queryIDToKill) + handleKillQuery(id) { + const {source, killQuery} = this.props + killQuery(source.links.proxy, id) } } diff --git a/ui/src/style/pages/admin.scss b/ui/src/style/pages/admin.scss index 4a06553a0e..115b5a7800 100644 --- a/ui/src/style/pages/admin.scss +++ b/ui/src/style/pages/admin.scss @@ -11,14 +11,14 @@ ---------------------------------------------- */ .admin-tabs { - padding-right: 0; + padding-right: 0; - & + div { - padding-left: 0; + & + div { + padding-left: 0; - .panel { - border-top-left-radius: 0; - } + .panel { + border-top-left-radius: 0; + } .panel-body { min-height: 300px; } @@ -27,7 +27,7 @@ font-weight: 400 !important; color: $g12-forge; } - } + } } .admin-tabs .btn-group { margin: 0; @@ -75,6 +75,9 @@ width: 100%; min-width: 150px; } + .admin-table--kill-button { + width: 70px; + } .admin-table--hidden { visibility: hidden; } @@ -188,7 +191,7 @@ } .db-manager-header--edit { justify-content: flex-start; - + .form-control { height: 22px; padding: 0 6px; @@ -219,4 +222,4 @@ font-size: 12px; width: 120px; } -} \ No newline at end of file +} From c5d503ed1f235a74caead6a4d08afb22a81b5e34 Mon Sep 17 00:00:00 2001 From: lukevmorris Date: Wed, 5 Apr 2017 14:29:19 -0700 Subject: [PATCH 80/81] Repair QueryBuilder in Safari (#1178) * Repair QueryBuilder in Safari * Margin was moved to .query-builder * Update CHANGELOG --- CHANGELOG.md | 1 + ui/src/style/components/resizer.scss | 4 +++- ui/src/style/pages/dashboards.scss | 5 +---- ui/src/style/pages/data-explorer/query-builder.scss | 5 +---- ui/src/style/pages/data-explorer/query-editor.scss | 2 -- 5 files changed, 6 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4fcb85d852..083c13fe11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ 1. [#1164](https://github.com/influxdata/chronograf/pull/1164): Restore ability to save raw queries to a Dashboard Cell 1. [#1115](https://github.com/influxdata/chronograf/pull/1115): Fix Basepath issue where content would fail to render under certain circumstances 1. [#1173](https://github.com/influxdata/chronograf/pull/1173): Fix saving email in Kapacitor alerts + 1. [#1178](https://github.com/influxdata/chronograf/pull/1178): Repair DataExplorer+CellEditorOverlay's QueryBuilder in Safari 1. [#979](https://github.com/influxdata/chronograf/issues/979): Fix empty tags for non-default retention policies 1. [#1179](https://github.com/influxdata/chronograf/pull/1179): Admin Databases Page will render a database without retention policies diff --git a/ui/src/style/components/resizer.scss b/ui/src/style/components/resizer.scss index 9de7e84706..74fc73ea36 100644 --- a/ui/src/style/components/resizer.scss +++ b/ui/src/style/components/resizer.scss @@ -93,6 +93,7 @@ $resizer-color-active: $c-pool; flex-direction: column; position: absolute; top: 0; + left: 0; height: 60%; width: 100%; } @@ -102,6 +103,7 @@ $resizer-color-active: $c-pool; position: absolute; height: 40%; bottom: 0; + left: 0; width: 100%; } -} \ No newline at end of file +} diff --git a/ui/src/style/pages/dashboards.scss b/ui/src/style/pages/dashboards.scss index e49e858130..e91ca185c3 100644 --- a/ui/src/style/pages/dashboards.scss +++ b/ui/src/style/pages/dashboards.scss @@ -420,11 +420,8 @@ $overlay-bg: rgba($c-pool, 0.7); @include gradient-h($g3-castle,$overlay-controls-bg); /* Hack for making the adjacent query builder have less margin on top */ - & + .query-builder .query-builder--tabs, - & + .query-builder .query-builder--tab-contents, - & + .query-builder .qeditor--empty { + & + .query-builder { margin-top: 2px; - height: calc(100% - 18px); } } .overlay-controls--right { diff --git a/ui/src/style/pages/data-explorer/query-builder.scss b/ui/src/style/pages/data-explorer/query-builder.scss index cfee43697c..7a59d46995 100644 --- a/ui/src/style/pages/data-explorer/query-builder.scss +++ b/ui/src/style/pages/data-explorer/query-builder.scss @@ -1,6 +1,7 @@ .query-builder { position: relative; flex: 1 0 0; + margin: 16px 0; width: calc(100% - #{($explorer-page-padding * 2)}); left: $explorer-page-padding; border: 0; @@ -13,8 +14,6 @@ .query-builder--tabs { display: flex; width: 250px; - margin-top: $de-vertical-margin; - height: calc(100% - #{($de-vertical-margin * 2)}); flex-direction: column; align-items: stretch; @include gradient-v($g3-castle,$g1-raven); @@ -147,8 +146,6 @@ $query-builder--column-heading-height: 50px; .query-builder--tab-contents { width: 100%; - margin-top: $de-vertical-margin; - height: calc(100% - #{($de-vertical-margin * 2)}); background-color: $g4-onyx; border-radius: 0 $radius $radius 0; overflow: hidden; diff --git a/ui/src/style/pages/data-explorer/query-editor.scss b/ui/src/style/pages/data-explorer/query-editor.scss index a1eb46824e..0f1e9ef61e 100644 --- a/ui/src/style/pages/data-explorer/query-editor.scss +++ b/ui/src/style/pages/data-explorer/query-editor.scss @@ -169,11 +169,9 @@ flex-direction: column; align-items: center; justify-content: center; - height: calc(100% - 32px); background-color: transparent; background-color: $g3-castle; border-radius: 0 $radius $radius 0; - margin-top: 16px; &, & > * { @include no-user-select(); From df8df318349f411feb1cfcf32880d0d0e6bc52ca Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Wed, 5 Apr 2017 15:19:36 -0700 Subject: [PATCH 81/81] Fix ghost dashboards (#1186) * Fix ghost dashboards * Remove dashboard key from reducer * Remove dashboard key from state * Update CHANGELOG --- CHANGELOG.md | 1 + ui/spec/dashboards/reducers/uiSpec.js | 47 +++------------- ui/src/dashboards/actions/index.js | 26 ++++----- ui/src/dashboards/containers/DashboardPage.js | 53 +++++++------------ ui/src/dashboards/reducers/ui.js | 34 +++--------- ui/src/shared/components/AutoRefresh.js | 2 +- 6 files changed, 44 insertions(+), 119 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d8ca2af8b..92b9643939 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ 1. [#1178](https://github.com/influxdata/chronograf/pull/1178): Repair DataExplorer+CellEditorOverlay's QueryBuilder in Safari 1. [#979](https://github.com/influxdata/chronograf/issues/979): Fix empty tags for non-default retention policies 1. [#1179](https://github.com/influxdata/chronograf/pull/1179): Admin Databases Page will render a database without retention policies + 1. [#1128](https://github.com/influxdata/chronograf/pull/1128): No more ghost dashboards 👻 1. [#1189](https://github.com/influxdata/chronograf/pull/1189): Clicking inside the graph header edit box will no longer blur the field. Use the Escape key for that behavior instead. ### Features diff --git a/ui/spec/dashboards/reducers/uiSpec.js b/ui/spec/dashboards/reducers/uiSpec.js index 2387a60ac6..2a10a3bfc3 100644 --- a/ui/spec/dashboards/reducers/uiSpec.js +++ b/ui/spec/dashboards/reducers/uiSpec.js @@ -1,12 +1,9 @@ import _ from 'lodash' import reducer from 'src/dashboards/reducers/ui' -import timeRanges from 'hson!src/shared/data/timeRanges.hson' import { loadDashboards, - setDashboard, - deleteDashboard, deleteDashboardFailed, setTimeRange, updateDashboardCells, @@ -15,12 +12,7 @@ import { syncDashboardCell, } from 'src/dashboards/actions' -const noopAction = () => { - return {type: 'NOOP'} -} - let state -const timeRange = timeRanges[1] const d1 = {id: 1, cells: [], name: "d1"} const d2 = {id: 2, cells: [], name: "d2"} const dashboards = [d1, d2] @@ -40,26 +32,9 @@ describe('DataExplorer.Reducers.UI', () => { const actual = reducer(state, loadDashboards(dashboards, d1.id)) const expected = { dashboards, - dashboard: d1, } expect(actual.dashboards).to.deep.equal(expected.dashboards) - expect(actual.dashboard).to.deep.equal(expected.dashboard) - }) - - it('can set a dashboard', () => { - const loadedState = reducer(state, loadDashboards(dashboards, d1.id)) - const actual = reducer(loadedState, setDashboard(d2.id)) - - expect(actual.dashboard).to.deep.equal(d2) - }) - - it('can handle a successful dashboard deletion', () => { - const loadedState = reducer(state, loadDashboards(dashboards)) - const expected = [d1] - const actual = reducer(loadedState, deleteDashboard(d2)) - - expect(actual.dashboards).to.deep.equal(expected) }) it('can handle a failed dashboard deletion', () => { @@ -82,34 +57,30 @@ describe('DataExplorer.Reducers.UI', () => { it('can update dashboard cells', () => { state = { - dashboard: d1, dashboards, } - const cells = [{id: 1}, {id: 2}] + const updatedCells = [{id: 1}, {id: 2}] const expected = { id: 1, - cells, + cells: updatedCells, name: 'd1', } - const actual = reducer(state, updateDashboardCells(cells)) + const actual = reducer(state, updateDashboardCells(d1, updatedCells)) - expect(actual.dashboard).to.deep.equal(expected) expect(actual.dashboards[0]).to.deep.equal(expected) }) - it('can edit cell', () => { + it('can edit a cell', () => { const dash = {...d1, cells} state = { - dashboard: dash, dashboards: [dash], } - const actual = reducer(state, editDashboardCell(0, 0, true)) + const actual = reducer(state, editDashboardCell(dash, 0, 0, true)) expect(actual.dashboards[0].cells[0].isEditing).to.equal(true) - expect(actual.dashboard.cells[0].isEditing).to.equal(true) }) it('can sync a cell', () => { @@ -121,25 +92,21 @@ describe('DataExplorer.Reducers.UI', () => { } const dash = {...d1, cells: [c1]} state = { - dashboard: dash, dashboards: [dash], } - const actual = reducer(state, syncDashboardCell(newCell)) + const actual = reducer(state, syncDashboardCell(dash, newCell)) expect(actual.dashboards[0].cells[0].name).to.equal(newCellName) - expect(actual.dashboard.cells[0].name).to.equal(newCellName) }) it('can rename cells', () => { const c2 = {...c1, isEditing: true} const dash = {...d1, cells: [c2]} state = { - dashboard: dash, dashboards: [dash], } - const actual = reducer(state, renameDashboardCell(0, 0, "Plutonium Consumption Rate (ug/sec)")) + const actual = reducer(state, renameDashboardCell(dash, 0, 0, "Plutonium Consumption Rate (ug/sec)")) expect(actual.dashboards[0].cells[0].name).to.equal("Plutonium Consumption Rate (ug/sec)") - expect(actual.dashboard.cells[0].name).to.equal("Plutonium Consumption Rate (ug/sec)") }) }) diff --git a/ui/src/dashboards/actions/index.js b/ui/src/dashboards/actions/index.js index ae8a0a8caf..3258f0376e 100644 --- a/ui/src/dashboards/actions/index.js +++ b/ui/src/dashboards/actions/index.js @@ -20,13 +20,6 @@ export const loadDashboards = (dashboards, dashboardID) => ({ }, }) -export const setDashboard = (dashboardID) => ({ - type: 'SET_DASHBOARD', - payload: { - dashboardID, - }, -}) - export const setTimeRange = (timeRange) => ({ type: 'SET_DASHBOARD_TIME_RANGE', payload: { @@ -55,16 +48,18 @@ export const deleteDashboardFailed = (dashboard) => ({ }, }) -export const updateDashboardCells = (cells) => ({ +export const updateDashboardCells = (dashboard, cells) => ({ type: 'UPDATE_DASHBOARD_CELLS', payload: { + dashboard, cells, }, }) -export const syncDashboardCell = (cell) => ({ +export const syncDashboardCell = (dashboard, cell) => ({ type: 'SYNC_DASHBOARD_CELL', payload: { + dashboard, cell, }, }) @@ -76,22 +71,24 @@ export const addDashboardCell = (cell) => ({ }, }) -export const editDashboardCell = (x, y, isEditing) => ({ +export const editDashboardCell = (dashboard, x, y, isEditing) => ({ type: 'EDIT_DASHBOARD_CELL', // x and y coords are used as a alternative to cell ids, which are not // universally unique, and cannot be because React depends on a // quasi-predictable ID for keys. Since cells cannot overlap, coordinates act // as a suitable id payload: { + dashboard, x, // x-coord of the cell to be edited y, // y-coord of the cell to be edited isEditing, }, }) -export const renameDashboardCell = (x, y, name) => ({ +export const renameDashboardCell = (dashboard, x, y, name) => ({ type: 'RENAME_DASHBOARD_CELL', payload: { + dashboard, x, // x-coord of the cell to be renamed y, // y-coord of the cell to be renamed name, @@ -117,17 +114,16 @@ export const getDashboardsAsync = (dashboardID) => async (dispatch) => { } } -export const putDashboard = () => (dispatch, getState) => { - const {dashboardUI: {dashboard}} = getState() +export const putDashboard = (dashboard) => (dispatch) => { updateDashboardAJAX(dashboard).then(({data}) => { dispatch(updateDashboard(data)) }) } -export const updateDashboardCell = (cell) => (dispatch) => { +export const updateDashboardCell = (dashboard, cell) => (dispatch) => { return updateDashboardCellAJAX(cell) .then(({data}) => { - dispatch(syncDashboardCell(data)) + dispatch(syncDashboardCell(dashboard, data)) }) } diff --git a/ui/src/dashboards/containers/DashboardPage.js b/ui/src/dashboards/containers/DashboardPage.js index 8ed4e7a290..8f1b8fc440 100644 --- a/ui/src/dashboards/containers/DashboardPage.js +++ b/ui/src/dashboards/containers/DashboardPage.js @@ -40,7 +40,6 @@ const DashboardPage = React.createClass({ dashboardActions: shape({ putDashboard: func.isRequired, getDashboardsAsync: func.isRequired, - setDashboard: func.isRequired, setTimeRange: func.isRequired, addDashboardCellAsync: func.isRequired, editDashboardCell: func.isRequired, @@ -50,10 +49,6 @@ const DashboardPage = React.createClass({ id: number.isRequired, cells: arrayOf(shape({})).isRequired, })), - dashboard: shape({ - id: number.isRequired, - cells: arrayOf(shape({})).isRequired, - }), handleChooseAutoRefresh: func.isRequired, autoRefresh: number.isRequired, timeRange: shape({}).isRequired, @@ -90,27 +85,12 @@ const DashboardPage = React.createClass({ getDashboardsAsync(dashboardID) }, - componentWillReceiveProps(nextProps) { - const {location: {pathname}} = this.props - const { - location: {pathname: nextPathname}, - params: {dashboardID: nextID}, - dashboardActions: {setDashboard}, - } = nextProps - - if (nextPathname.pathname === pathname) { - return - } - - setDashboard(nextID) - }, - handleDismissOverlay() { this.setState({selectedCell: null}) }, handleSaveEditedCell(newCell) { - this.props.dashboardActions.updateDashboardCell(newCell) + this.props.dashboardActions.updateDashboardCell(this.getActiveDashboard(), newCell) .then(this.handleDismissOverlay) }, @@ -123,13 +103,13 @@ const DashboardPage = React.createClass({ }, handleUpdatePosition(cells) { - this.props.dashboardActions.updateDashboardCells(cells) - this.props.dashboardActions.putDashboard() + const dashboard = this.getActiveDashboard() + this.props.dashboardActions.updateDashboardCells(dashboard, cells) + this.props.dashboardActions.putDashboard(dashboard) }, handleAddCell() { - const {dashboard} = this.props - this.props.dashboardActions.addDashboardCellAsync(dashboard) + this.props.dashboardActions.addDashboardCellAsync(this.getActiveDashboard()) }, handleEditDashboard() { @@ -142,29 +122,28 @@ const DashboardPage = React.createClass({ handleRenameDashboard(name) { this.setState({isEditMode: false}) - const {dashboard} = this.props - const newDashboard = {...dashboard, name} + const newDashboard = {...this.getActiveDashboard(), name} this.props.dashboardActions.updateDashboard(newDashboard) - this.props.dashboardActions.putDashboard() + this.props.dashboardActions.putDashboard(newDashboard) }, // Places cell into editing mode. handleEditDashboardCell(x, y, isEditing) { return () => { - this.props.dashboardActions.editDashboardCell(x, y, !isEditing) /* eslint-disable no-negated-condition */ + this.props.dashboardActions.editDashboardCell(this.getActiveDashboard(), x, y, !isEditing) /* eslint-disable no-negated-condition */ } }, handleRenameDashboardCell(x, y) { return (evt) => { - this.props.dashboardActions.renameDashboardCell(x, y, evt.target.value) + this.props.dashboardActions.renameDashboardCell(this.getActiveDashboard(), x, y, evt.target.value) } }, handleUpdateDashboardCell(newCell) { return () => { this.props.dashboardActions.editDashboardCell(newCell.x, newCell.y, false) - this.props.dashboardActions.putDashboard() + this.props.dashboardActions.putDashboard(this.getActiveDashboard()) } }, @@ -172,11 +151,15 @@ const DashboardPage = React.createClass({ this.props.dashboardActions.deleteDashboardCellAsync(cell) }, + getActiveDashboard() { + const {params: {dashboardID}, dashboards} = this.props + return dashboards.find(d => d.id === +dashboardID) + }, + render() { const { dashboards, - dashboard, - params: {sourceID}, + params: {sourceID, dashboardID}, inPresentationMode, handleClickPresentationButton, source, @@ -185,6 +168,8 @@ const DashboardPage = React.createClass({ timeRange, } = this.props + const dashboard = dashboards.find(d => d.id === +dashboardID) + const { selectedCell, isEditMode, @@ -269,14 +254,12 @@ const mapStateToProps = (state) => { }, dashboardUI: { dashboards, - dashboard, timeRange, }, } = state return { dashboards, - dashboard, autoRefresh, timeRange, inPresentationMode, diff --git a/ui/src/dashboards/reducers/ui.js b/ui/src/dashboards/reducers/ui.js index e1d8d16087..2f7b15cabd 100644 --- a/ui/src/dashboards/reducers/ui.js +++ b/ui/src/dashboards/reducers/ui.js @@ -1,12 +1,10 @@ import _ from 'lodash' -import {EMPTY_DASHBOARD} from 'src/dashboards/constants' import timeRanges from 'hson!../../shared/data/timeRanges.hson' const {lower, upper} = timeRanges[1] const initialState = { - dashboards: null, - dashboard: EMPTY_DASHBOARD, + dashboards: [], timeRange: {lower, upper}, isEditMode: false, } @@ -14,19 +12,9 @@ const initialState = { export default function ui(state = initialState, action) { switch (action.type) { case 'LOAD_DASHBOARDS': { - const {dashboards, dashboardID} = action.payload + const {dashboards} = action.payload const newState = { dashboards, - dashboard: _.find(dashboards, (d) => d.id === +dashboardID), - } - - return {...state, ...newState} - } - - case 'SET_DASHBOARD': { - const {dashboardID} = action.payload - const newState = { - dashboard: _.find(state.dashboards, (d) => d.id === +dashboardID), } return {...state, ...newState} @@ -69,8 +57,7 @@ export default function ui(state = initialState, action) { } case 'UPDATE_DASHBOARD_CELLS': { - const {cells} = action.payload - const {dashboard} = state + const {cells, dashboard} = action.payload const newDashboard = { ...dashboard, @@ -78,7 +65,6 @@ export default function ui(state = initialState, action) { } const newState = { - dashboard: newDashboard, dashboards: state.dashboards.map((d) => d.id === dashboard.id ? newDashboard : d), } @@ -93,7 +79,6 @@ export default function ui(state = initialState, action) { const newDashboard = {...dashboard, cells: newCells} const newDashboards = dashboards.map((d) => d.id === dashboard.id ? newDashboard : d) const newState = { - dashboard: newDashboard, dashboards: newDashboards, } @@ -101,8 +86,7 @@ export default function ui(state = initialState, action) { } case 'EDIT_DASHBOARD_CELL': { - const {x, y, isEditing} = action.payload - const {dashboard} = state + const {x, y, isEditing, dashboard} = action.payload const cell = dashboard.cells.find((c) => c.x === x && c.y === y) @@ -117,7 +101,6 @@ export default function ui(state = initialState, action) { } const newState = { - dashboard: newDashboard, dashboards: state.dashboards.map((d) => d.id === dashboard.id ? newDashboard : d), } @@ -134,7 +117,6 @@ export default function ui(state = initialState, action) { cells: newCells, } const newState = { - dashboard: newDashboard, dashboards: state.dashboards.map((d) => d.id === dashboard.id ? newDashboard : d), } @@ -142,8 +124,7 @@ export default function ui(state = initialState, action) { } case 'SYNC_DASHBOARD_CELL': { - const {cell} = action.payload - const {dashboard} = state + const {cell, dashboard} = action.payload const newDashboard = { ...dashboard, @@ -151,7 +132,6 @@ export default function ui(state = initialState, action) { } const newState = { - dashboard: newDashboard, dashboards: state.dashboards.map((d) => d.id === dashboard.id ? newDashboard : d), } @@ -159,8 +139,7 @@ export default function ui(state = initialState, action) { } case 'RENAME_DASHBOARD_CELL': { - const {x, y, name} = action.payload - const {dashboard} = state + const {x, y, name, dashboard} = action.payload const cell = dashboard.cells.find((c) => c.x === x && c.y === y) @@ -175,7 +154,6 @@ export default function ui(state = initialState, action) { } const newState = { - dashboard: newDashboard, dashboards: state.dashboards.map((d) => d.id === dashboard.id ? newDashboard : d), } diff --git a/ui/src/shared/components/AutoRefresh.js b/ui/src/shared/components/AutoRefresh.js index 1578472235..729980758a 100644 --- a/ui/src/shared/components/AutoRefresh.js +++ b/ui/src/shared/components/AutoRefresh.js @@ -17,7 +17,6 @@ const { export default function AutoRefresh(ComposedComponent) { const wrapper = React.createClass({ - displayName: `AutoRefresh_${ComposedComponent.displayName}`, propTypes: { children: element, autoRefresh: number.isRequired, @@ -87,6 +86,7 @@ export default function AutoRefresh(ComposedComponent) { }, componentWillUnmount() { clearInterval(this.intervalID) + this.intervalID = false }, render() { const {timeSeries} = this.state