diff --git a/.jshintrc b/.jshintrc index 836165107b094add85329282f9d4353e24288727..5fec253525d7255e86fd5584711d3857cdf694c0 100644 --- a/.jshintrc +++ b/.jshintrc @@ -3,6 +3,7 @@ "globals": { "angular": false, "console": false, + "React": false, "Set": false } } diff --git a/circle.yml b/circle.yml index 2ece3029d06d2b84c00bd4709cccb47092024360..032e020958650c9221a14a6fd89f191081ee7aa4 100644 --- a/circle.yml +++ b/circle.yml @@ -4,5 +4,5 @@ machine: oraclejdk8 test: override: - - case $CIRCLE_NODE_INDEX in 0) lein eastwood ;; 1) lein test ;; 2) lein uberjar ;; 3) ./util/lint_js.sh && lein bikeshed --max-line-length 240 ;; esac: + - case $CIRCLE_NODE_INDEX in 0) lein eastwood ;; 1) lein test ;; 2) lein uberjar ;; 3) ./lint_js.sh && lein bikeshed --max-line-length 240 ;; esac: parallel: true diff --git a/util/lint_js.sh b/lint_js.sh similarity index 91% rename from util/lint_js.sh rename to lint_js.sh index 7e2572ce00dd00bc4473c215ea33504fd53dd419..77c55be5d32d9c6248caf2cbfeb2540a9bad85f8 100755 --- a/util/lint_js.sh +++ b/lint_js.sh @@ -2,9 +2,9 @@ # Simple shell script for running jshint and nicely formatting the output -JS_HINT=./node_modules/jshint/bin/jshint +JS_HINT=./node_modules/jsxhint/cli.js JS_HINT_OPTS='--config .jshintrc' -JS_FILES=`find resources/frontend_client/app -name "*.js" | grep -v bower_components | grep -v 'app/test/' | grep -v '\#' | grep -v 'app/dist/' | grep -v react` +JS_FILES=`find resources/frontend_client/app -name "*.js*" | grep -v bower_components | grep -v 'app/test/' | grep -v '\#' | grep -v 'app/dist/'` BOLD='\033[1;30m' RED='\033[0;31m' # \e doesn't work on OS X but \033 works on either diff --git a/package.json b/package.json index b2b133457e1ecc31ffc7cc65f4f8eb18651800a5..1ae2d45295baf1af193a9d009334e91d589e320e 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ }, "devDependencies": { "http-server": "^0.6.1", - "jshint": "2.6.3", + "jsxhint": "0.13.3", "karma": "~0.10", "karma-junit-reporter": "^0.2.2", "protractor": "~0.20.1", diff --git a/resources/frontend_client/app/query_builder/database_selector.react.js b/resources/frontend_client/app/query_builder/database_selector.react.js index 4beae68faa30d2fe8f891da84b80d27f6beb30a8..8509c7629b1f0f6b65f7d9d60b27927e4cd57ded 100644 --- a/resources/frontend_client/app/query_builder/database_selector.react.js +++ b/resources/frontend_client/app/query_builder/database_selector.react.js @@ -1,4 +1,5 @@ 'use strict'; +/*global SelectionModule*/ var DatabaseSelector = React.createClass({ displayName: 'DatabaseSelector', diff --git a/resources/frontend_client/app/query_builder/date_filter.react.js b/resources/frontend_client/app/query_builder/date_filter.react.js index 6bd6dbd126f2a20727615d4efdf9d9c0bfae1e16..cfcbe9f451efb6550e3e8912b8e6f98a32c6be53 100644 --- a/resources/frontend_client/app/query_builder/date_filter.react.js +++ b/resources/frontend_client/app/query_builder/date_filter.react.js @@ -1,4 +1,5 @@ 'use strict'; +/*global moment, DatePicker*/ var DateFilter = React.createClass({ displayName: 'DateFilter', diff --git a/resources/frontend_client/app/query_builder/filter_widget.react.js b/resources/frontend_client/app/query_builder/filter_widget.react.js index cb582a385166a46e65d4423dffcd6db062660260..30461a3664f23442e5b49e78f563cfe0e0c38a0b 100644 --- a/resources/frontend_client/app/query_builder/filter_widget.react.js +++ b/resources/frontend_client/app/query_builder/filter_widget.react.js @@ -1,4 +1,5 @@ 'use strict'; +/*global DateFilter, SelectionModule*/ var FilterWidget = React.createClass({ displayName: 'FilterWidget', @@ -18,7 +19,7 @@ var FilterWidget = React.createClass({ this.props.updateFilter(value, 2, index); }, _isOpen: function (value) { - if(value != undefined) { + if (value !== undefined) { return true; } else { return false; @@ -56,7 +57,7 @@ var FilterWidget = React.createClass({ parentIndex={this.props.index} /> </div> - ) + ); }, _getSafeValues: function () { return this.props.valueFields.values.map(function(value) { @@ -106,7 +107,7 @@ var FilterWidget = React.createClass({ date.format('YYYY-MM-DD'), 2, this.props.index - ) + ); }.bind(this) } /> diff --git a/resources/frontend_client/app/query_builder/header.react.js b/resources/frontend_client/app/query_builder/header.react.js index d61d09aa13cc0221ef8c5bb251e4c27f0f67ba71..7f9d41b3fa746c254fc0f17760df44de811ef17d 100644 --- a/resources/frontend_client/app/query_builder/header.react.js +++ b/resources/frontend_client/app/query_builder/header.react.js @@ -12,6 +12,6 @@ var QueryHeader = React.createClass({ var name = this.props.name || "What would you like to know?"; return ( <h1 className="QueryName">{name}</h1> - ) + ); } }); diff --git a/resources/frontend_client/app/query_builder/query_picker.react.js b/resources/frontend_client/app/query_builder/query_picker.react.js index 0e9e4a8ce473cd304420b73fffa36c8cca356398..fe36e4d51e66fafc2b5ef94ed490429fa58e8d39 100644 --- a/resources/frontend_client/app/query_builder/query_picker.react.js +++ b/resources/frontend_client/app/query_builder/query_picker.react.js @@ -1,4 +1,5 @@ 'use strict'; +/*global SelectionModule, DatabaseSelector*/ var QueryPicker = React.createClass({ displayName: 'QueryPicker', @@ -43,7 +44,7 @@ var QueryPicker = React.createClass({ if(this.props.aggregationComplete() && this.props.options.breakout_options.fields.length > 0) { - (this.props.query.breakout.length < 1) ? addDimensionButtonText = "Grouped by" : addDimensionButtonText = "and"; + addDimensionButtonText = (this.props.query.breakout.length < 1) ? "Grouped by" : "and"; if(this.props.query.breakout.length < 2) { addDimensionButton = ( @@ -104,7 +105,7 @@ var QueryPicker = React.createClass({ // if there's a value in the second aggregation slot if(this.props.query.aggregation.length > 1) { if(this.props.query.aggregation[1] !== null) { - aggregationTargetListOpen = false + aggregationTargetListOpen = false; } aggregationTargetHtml = ( <SelectionModule diff --git a/resources/frontend_client/app/query_builder/saver.react.js b/resources/frontend_client/app/query_builder/saver.react.js index e62b0fab055950dc7e0c5118d9c70884513c93f0..e10192c5d28f6a91b1cee384468da1b11be52f74 100644 --- a/resources/frontend_client/app/query_builder/saver.react.js +++ b/resources/frontend_client/app/query_builder/saver.react.js @@ -1,3 +1,6 @@ +'use strict'; +/*global cx, OnClickOutside, SelectionModule*/ + var Saver = React.createClass({ displayName: 'Saver', propTypes: { @@ -16,7 +19,7 @@ var Saver = React.createClass({ }; }, handleClickOutside: function () { - this.replaceState(this.getInitialState()) + this.replaceState(this.getInitialState()); }, _openModal: function () { this.setState({ @@ -24,22 +27,22 @@ var Saver = React.createClass({ triggerAction: this._save }, function () { // focus the name field - this.refs.name.getDOMNode().focus() - }) + this.refs.name.getDOMNode().focus(); + }); }, _save: function () { var name = this.refs.name.getDOMNode().value, - description = this.refs.description.getDOMNode().value + description = this.refs.description.getDOMNode().value; this.props.save({ name: name, description: description - }) + }); // reset the modal this.setState({ modalOpen: false, triggerAction: this._openModal - }) + }); }, render: function () { var buttonClasses = cx({ @@ -47,19 +50,19 @@ var Saver = React.createClass({ 'Button': true, 'block': true, 'Button--primary': this.state.modalOpen - }) + }); var modalClasses = cx({ 'SaveModal': true, 'Modal--showing': this.state.modalOpen - }) + }); var buttonText; // if the query has changed or the modal has been opened - if(this.props.hasChanged == true || this.state.modalOpen == true) { - buttonText = "Save" + if (this.props.hasChanged === true || this.state.modalOpen === true) { + buttonText = "Save"; } else { - buttonText = "Edit" + buttonText = "Edit"; } var privacyOptions = [ @@ -102,6 +105,6 @@ var Saver = React.createClass({ {buttonText} </a> </div> - ) + ); } }); diff --git a/resources/frontend_client/app/query_builder/selection_module.react.js b/resources/frontend_client/app/query_builder/selection_module.react.js index e1e9cb2bd6271e5b967e9c45678424b65f149267..3bb0c917f578ea09f763618d6c8fa4e8b6f2f2e4 100644 --- a/resources/frontend_client/app/query_builder/selection_module.react.js +++ b/resources/frontend_client/app/query_builder/selection_module.react.js @@ -1,4 +1,5 @@ 'use strict'; +/*global cx, OnClickOutside, SearchBar*/ var SelectionModule = React.createClass({ displayName:'SelectionModule', @@ -23,7 +24,7 @@ var SelectionModule = React.createClass({ searchThreshold: 20, searchEnabled: false, filterTerm: null - } + }; }, handleClickOutside: function () { this.setState({ @@ -84,9 +85,9 @@ var SelectionModule = React.createClass({ _select: function (item) { var index = this.props.index; // send back the item with the specified action - if(this.props.action) { - if(index != undefined) { - if(this.props.parentIndex) { + if (this.props.action) { + if (index !== undefined) { + if (this.props.parentIndex) { this.props.action(item[this.props.selectedKey], index, this.props.parentIndex); } else { this.props.action(item[this.props.selectedKey], index); @@ -127,13 +128,13 @@ var SelectionModule = React.createClass({ }); if(this._enableSearch()) { - searchBar = <SearchBar onFilter={this._filterSelections} /> + searchBar = <SearchBar onFilter={this._filterSelections} />; } if(this.props.remove) { var style = { fill: '#ddd' - } + }; remove = ( <a className="RemoveTrigger" href="#" onClick={this.props.remove.bind(null, this.props.index)}> <svg className="geomicon" data-icon="close" viewBox="0 0 32 32" style={style} width="16px" height="16px"> diff --git a/resources/frontend_client/app/query_builder/visualization.react.js b/resources/frontend_client/app/query_builder/visualization.react.js index 609cda9872ffb900dac564564d4a876cd36d6cdb..a43ad9534ac01bcec78735962602bd268b0792a8 100644 --- a/resources/frontend_client/app/query_builder/visualization.react.js +++ b/resources/frontend_client/app/query_builder/visualization.react.js @@ -1,4 +1,5 @@ 'use strict'; +/*global cx, CardRenderer*/ var QueryVisualization = React.createClass({ displayName: 'QueryVisualization', @@ -40,7 +41,7 @@ var QueryVisualization = React.createClass({ if(this.state.type === 'table') { tableRows = this.props.result.data.rows.map(function (row) { var rowCols = row.map(function (data) { - return (<td>{data.toString()}</td>) + return (<td>{data.toString()}</td>); }); return (<tr>{rowCols}</tr>); @@ -98,7 +99,7 @@ var QueryVisualization = React.createClass({ var buttonClasses = cx({ 'Button': true, 'Button--primary' : (type == this.state.type) - }) + }); return ( <a className={buttonClasses} href="#" onClick={this._changeType.bind(null, type)}>{type}</a> );