diff --git a/ui/src/dashboards/components/CellEditorOverlay.js b/ui/src/dashboards/components/CellEditorOverlay.js index 879bfb716..764f5870e 100644 --- a/ui/src/dashboards/components/CellEditorOverlay.js +++ b/ui/src/dashboards/components/CellEditorOverlay.js @@ -4,7 +4,7 @@ import _ from 'lodash' import uuid from 'node-uuid' import ResizeContainer, {ResizeBottom} from 'src/shared/components/ResizeContainer' -import QueryBuilder from 'src/data_explorer/components/QueryBuilder' +import QueryMaker from 'src/data_explorer/components/QueryMaker' import Visualization from 'src/data_explorer/components/Visualization' import OverlayControls from 'src/dashboards/components/OverlayControls' import * as queryModifiers from 'src/utils/queryTransitions' @@ -159,7 +159,7 @@ class CellEditorOverlay extends Component { onCancel={onCancel} onSave={this.handleSaveCell} /> - { const {onCancel, onSave, selectedGraphType, onSelectGraphType} = props return (
-

Graph Editor

+

Cell Editor

Visualization Type:

    diff --git a/ui/src/data_explorer/components/DatabaseList.js b/ui/src/data_explorer/components/DatabaseList.js index 985ba2ded..87fbcbd0c 100644 --- a/ui/src/data_explorer/components/DatabaseList.js +++ b/ui/src/data_explorer/components/DatabaseList.js @@ -61,19 +61,19 @@ const DatabaseList = React.createClass({ return (
    -
    Databases
    -
      +
      Databases
      +
      {this.state.namespaces.map((namespace) => { const {database, retentionPolicy} = namespace const isActive = database === query.database && retentionPolicy === query.retentionPolicy return ( -
    • +
      {database}.{retentionPolicy} -
    • +
      ) })} -
    +
) }, diff --git a/ui/src/data_explorer/components/FieldList.js b/ui/src/data_explorer/components/FieldList.js index b1d2fe011..36ed5448f 100644 --- a/ui/src/data_explorer/components/FieldList.js +++ b/ui/src/data_explorer/components/FieldList.js @@ -74,17 +74,12 @@ const FieldList = React.createClass({ return (
-
Fields
- { - hasAggregates ? -
-
-

Group by Time

- -
-
- : null - } +
+ Fields + {hasAggregates ? + + : null} +
{this.renderList()}
) @@ -93,11 +88,15 @@ const FieldList = React.createClass({ renderList() { const {database, measurement} = this.props.query if (!database || !measurement) { - return
No Measurement selected
+ return ( +
+ No Measurement selected +
+ ) } return ( - +
) }, diff --git a/ui/src/data_explorer/components/FieldListItem.js b/ui/src/data_explorer/components/FieldListItem.js index 3cc1cf02a..80b151695 100644 --- a/ui/src/data_explorer/components/FieldListItem.js +++ b/ui/src/data_explorer/components/FieldListItem.js @@ -39,16 +39,17 @@ const FieldListItem = React.createClass({ }) return ( -
  • - {fieldText} -
    - { - isKapacitorRule ? - : - - } -
    -
  • +
    + +
    + {fieldText} +
    + { + isKapacitorRule ? + : + + } +
    ) }, }) diff --git a/ui/src/data_explorer/components/GroupByTimeDropdown.js b/ui/src/data_explorer/components/GroupByTimeDropdown.js index 23a1a7bd5..c43cb542c 100644 --- a/ui/src/data_explorer/components/GroupByTimeDropdown.js +++ b/ui/src/data_explorer/components/GroupByTimeDropdown.js @@ -16,16 +16,16 @@ const GroupByTimeDropdown = React.createClass({ const {isOpen, selected, onChooseGroupByTime} = this.props return ( -
    +
    - {selected || '...'} - + Group by {selected || 'time'} +
    -
      +
        {groupByTimeOptions.map((groupBy) => { return ( -
      • - onChooseGroupByTime(groupBy)}> +
      • onChooseGroupByTime(groupBy)}> + {groupBy.menuOption}
      • diff --git a/ui/src/data_explorer/components/MeasurementList.js b/ui/src/data_explorer/components/MeasurementList.js index 943a08b9f..ac719e53b 100644 --- a/ui/src/data_explorer/components/MeasurementList.js +++ b/ui/src/data_explorer/components/MeasurementList.js @@ -72,11 +72,15 @@ const MeasurementList = React.createClass({ render() { return (
        -
        Measurements
        - {this.props.query.database ?
        - - -
        : null } +
        + Measurements + {this.props.query.database ? +
        + + +
        + : null } +
        {this.renderList()}
        ) @@ -84,20 +88,24 @@ const MeasurementList = React.createClass({ renderList() { if (!this.props.query.database) { - return
        No Database selected
        + return ( +
        + No Database selected +
        + ) } const measurements = this.state.measurements.filter((m) => m.match(this.state.filterText)) return ( -
          +
          {measurements.map((measurement) => { const isActive = measurement === this.props.query.measurement return ( -
        • {measurement}
        • +
          {measurement}
          ) })} -
        +
    ) }, diff --git a/ui/src/data_explorer/components/QueryBuilder.js b/ui/src/data_explorer/components/QueryBuilder.js index 7b1caa9d9..d16b1c96e 100644 --- a/ui/src/data_explorer/components/QueryBuilder.js +++ b/ui/src/data_explorer/components/QueryBuilder.js @@ -1,16 +1,16 @@ import React, {PropTypes} from 'react' +import DatabaseList from './DatabaseList' +import MeasurementList from './MeasurementList' +import FieldList from './FieldList' +import TagList from './TagList' import QueryEditor from './QueryEditor' -import QueryTabItem from './QueryTabItem' import buildInfluxQLQuery from 'utils/influxql' const { - arrayOf, - func, - node, - number, - shape, string, + shape, + func, } = PropTypes const QueryBuilder = React.createClass({ @@ -20,7 +20,9 @@ const QueryBuilder = React.createClass({ queries: string.isRequired, }).isRequired, }).isRequired, - queries: arrayOf(shape({})).isRequired, + query: shape({ + id: string, + }).isRequired, timeRange: shape({ upper: string, lower: string, @@ -28,103 +30,91 @@ const QueryBuilder = React.createClass({ actions: shape({ chooseNamespace: func.isRequired, chooseMeasurement: func.isRequired, + applyFuncsToField: func.isRequired, chooseTag: func.isRequired, groupByTag: func.isRequired, - addQuery: func.isRequired, toggleField: func.isRequired, groupByTime: func.isRequired, toggleTagAcceptance: func.isRequired, - applyFuncsToField: func.isRequired, editRawTextAsync: func.isRequired, }).isRequired, - height: string, - top: string, - setActiveQueryIndex: func.isRequired, - onDeleteQuery: func.isRequired, - activeQueryIndex: number, - children: node, }, - handleAddQuery() { - const newIndex = this.props.queries.length - this.props.actions.addQuery() - this.props.setActiveQueryIndex(newIndex) + handleChooseNamespace(namespace) { + this.props.actions.chooseNamespace(this.props.query.id, namespace) }, - handleAddRawQuery() { - const newIndex = this.props.queries.length - this.props.actions.addQuery({rawText: ''}) - this.props.setActiveQueryIndex(newIndex) + handleChooseMeasurement(measurement) { + this.props.actions.chooseMeasurement(this.props.query.id, measurement) }, - getActiveQuery() { - const {queries, activeQueryIndex} = this.props - const activeQuery = queries[activeQueryIndex] - const defaultQuery = queries[0] + handleToggleField(field) { + this.props.actions.toggleField(this.props.query.id, field) + }, - return activeQuery || defaultQuery + handleGroupByTime(time) { + this.props.actions.groupByTime(this.props.query.id, time) + }, + + handleApplyFuncsToField(fieldFunc) { + this.props.actions.applyFuncsToField(this.props.query.id, fieldFunc) + }, + + handleChooseTag(tag) { + this.props.actions.chooseTag(this.props.query.id, tag) + }, + + handleToggleTagAcceptance() { + this.props.actions.toggleTagAcceptance(this.props.query.id) + }, + + handleGroupByTag(tagKey) { + this.props.actions.groupByTag(this.props.query.id, tagKey) + }, + + handleEditRawText(text) { + const {source: {links}, query} = this.props + this.props.actions.editRawTextAsync(links.queries, query.id, text) }, render() { - const {height, top} = this.props + const {query, timeRange} = this.props + const q = query.rawText || buildInfluxQLQuery(timeRange, query) || '' + return ( -
    - {this.renderQueryTabList()} - {this.renderQueryEditor()} +
    + + {this.renderLists()}
    ) }, - renderQueryEditor() { - const {timeRange, actions, source} = this.props - const query = this.getActiveQuery() - if (!query) { - return ( -
    -
    This Graph has no Queries
    -
    -
    Add a Query
    -
    - ) - } + renderLists() { + const {query} = this.props return ( - - ) - }, - - renderQueryTabList() { - const {queries, activeQueryIndex, onDeleteQuery, timeRange, setActiveQueryIndex} = this.props - - return ( -
    -
    -

    Queries

    -
    - -
    -
    - {queries.map((q, i) => { - return ( - - ) - })} - {this.props.children} +
    + + + +
    ) }, diff --git a/ui/src/data_explorer/components/QueryEditor.js b/ui/src/data_explorer/components/QueryEditor.js index ebec754a9..341b78f42 100644 --- a/ui/src/data_explorer/components/QueryEditor.js +++ b/ui/src/data_explorer/components/QueryEditor.js @@ -1,122 +1,102 @@ import React, {PropTypes} from 'react' +import classNames from 'classnames' +import Dropdown from 'src/shared/components/Dropdown' +import LoadingDots from 'src/shared/components/LoadingDots' +import {QUERY_TEMPLATES} from 'src/data_explorer/constants' -import DatabaseList from './DatabaseList' -import MeasurementList from './MeasurementList' -import FieldList from './FieldList' -import TagList from './TagList' -import RawQueryEditor from './RawQueryEditor' -import buildInfluxQLQuery from 'utils/influxql' - +const ENTER = 13 +const ESCAPE = 27 const { - string, - shape, func, + shape, + string, } = PropTypes - const QueryEditor = React.createClass({ propTypes: { - source: shape({ - links: shape({ - queries: string.isRequired, - }).isRequired, - }).isRequired, - query: shape({ - id: string, - }).isRequired, - timeRange: shape({ - upper: string, - lower: string, - }).isRequired, - actions: shape({ - chooseNamespace: func.isRequired, - chooseMeasurement: func.isRequired, - applyFuncsToField: func.isRequired, - chooseTag: func.isRequired, - groupByTag: func.isRequired, - toggleField: func.isRequired, - groupByTime: func.isRequired, - toggleTagAcceptance: func.isRequired, - editRawTextAsync: func.isRequired, - }).isRequired, + query: string.isRequired, + onUpdate: func.isRequired, + config: shape().isRequired, }, - handleChooseNamespace(namespace) { - this.props.actions.chooseNamespace(this.props.query.id, namespace) + getInitialState() { + return { + value: this.props.query, + } }, - handleChooseMeasurement(measurement) { - this.props.actions.chooseMeasurement(this.props.query.id, measurement) + componentWillReceiveProps(nextProps) { + if (this.props.query !== nextProps.query) { + this.setState({value: nextProps.query}) + } }, - handleToggleField(field) { - this.props.actions.toggleField(this.props.query.id, field) + handleKeyDown(e) { + if (e.keyCode === ENTER) { + e.preventDefault() + this.handleUpdate() + } else if (e.keyCode === ESCAPE) { + this.setState({value: this.state.value}, () => { + this.editor.blur() + }) + } }, - handleGroupByTime(time) { - this.props.actions.groupByTime(this.props.query.id, time) + handleChange() { + this.setState({ + value: this.editor.value, + }) }, - handleApplyFuncsToField(fieldFunc) { - this.props.actions.applyFuncsToField(this.props.query.id, fieldFunc) + handleUpdate() { + this.props.onUpdate(this.state.value) }, - handleChooseTag(tag) { - this.props.actions.chooseTag(this.props.query.id, tag) - }, - - handleToggleTagAcceptance() { - this.props.actions.toggleTagAcceptance(this.props.query.id) - }, - - handleGroupByTag(tagKey) { - this.props.actions.groupByTag(this.props.query.id, tagKey) - }, - - handleEditRawText(text) { - const {source: {links}, query} = this.props - this.props.actions.editRawTextAsync(links.queries, query.id, text) + handleChooseTemplate(template) { + this.setState({value: template.query}) }, render() { - const {query, timeRange} = this.props - const q = query.rawText || buildInfluxQLQuery(timeRange, query) || '' + const {config: {status}} = this.props + const {value} = this.state return ( -
    -
    - - {this.renderLists()} -
    +
    +