From 3107cc611022a7afe74f31d2899db15c6fb1fcbf Mon Sep 17 00:00:00 2001 From: Ari Crellin-Quick Date: Tue, 17 Apr 2018 13:24:02 -0700 Subject: [PATCH 1/3] Run linter before tests on Travis --- .travis/travis_script.sh | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.travis/travis_script.sh b/.travis/travis_script.sh index a490d57..05442b1 100755 --- a/.travis/travis_script.sh +++ b/.travis/travis_script.sh @@ -3,10 +3,17 @@ set -ex +section "ESLint" + +make lint-install +make lint + +section_end "ESLint" + + section "Tests" make log & make ${TEST_TARGET} section_end "Tests" - From 4986f68141043c573bc55d93e4d9ef152bc3d6a1 Mon Sep 17 00:00:00 2001 From: Ari Crellin-Quick Date: Thu, 19 Apr 2018 18:25:51 -0700 Subject: [PATCH 2/3] Lint-roll all JavaScript code & update baselayer --- .eslintrc | 91 +++++----- baselayer | 2 +- package.json | 10 +- static/js/CesiumMessageHandler.js | 61 ++++--- static/js/actions.js | 240 ++++++++++++++------------- static/js/components/Datasets.jsx | 35 ++-- static/js/components/Delete.jsx | 14 +- static/js/components/Dot.jsx | 16 +- static/js/components/Expand.jsx | 14 +- static/js/components/Features.jsx | 89 ++++++---- static/js/components/FoldableRow.jsx | 6 +- static/js/components/Form.jsx | 39 +++-- static/js/components/Main.jsx | 37 +++-- static/js/components/Models.jsx | 68 ++++---- static/js/components/Plot.jsx | 19 ++- static/js/components/Predictions.jsx | 80 +++++---- static/js/components/Projects.jsx | 35 ++-- static/js/components/Tooltip.jsx | 13 +- static/js/components/UserProfile.jsx | 10 +- static/js/reducers.js | 33 ++-- static/js/validate.js | 2 - 21 files changed, 513 insertions(+), 401 deletions(-) diff --git a/.eslintrc b/.eslintrc index 2459981..572e228 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,48 +1,49 @@ { - "comment": "off = 0, warn = 1, error = 2", + "comment": "off = 0, warn = 1, error = 2", - "parser": "babel-eslint", - "env": { - "browser": true - }, - "extends": "airbnb", - "rules": { - "max-len": [1, 100, 2, {ignoreComments: true}], - "quote-props": [1, "consistent-as-needed"], - "no-cond-assign": [2, "except-parens"], - "radix": 0, - "space-infix-ops": 0, - "no-unused-vars": [1, {"vars": "local", "args": "none"}], - "default-case": 0, - "no-else-return": 0, - "no-param-reassign": 0, - "quotes": 0, - "comma-dangle": [1, "only-multiline"], - "vars-on-top": 0, - "prefer-const": 1, - "eqeqeq": 1, - "import/imports-first": 1, - "consistent-return": 0, - "import/no-mutable-exports": 0, - "no-class-assign": 0, - "jsx-a11y/label-has-for": 0, - "jsx-a11y/href-no-hash": 1, - "camelcase": 0, - "no-fallthrough": 0, - "no-restricted-syntax": 1, - "guard-for-in": 1, - "array-callback-return": 1, - "class-methods-use-this": 1, - "jsx-a11y/no-static-element-interactions": 1, - "react/forbid-prop-types": 1, - "arrow-parens": 1, - "no-bitwise": 1, - "no-plusplus": 1, - "no-extend-native": 1, - "no-use-before-define": 1, - "no-unused-expressions": 1, - "no-continue": 1, - "no-prototype-builtins": 1, - "dot-notation": 1, - } + "parser": "babel-eslint", + "env": { + "browser": true + }, + "extends": "airbnb", + "rules": { + "max-len": [1, 100, 2, {ignoreComments: true}], + "quote-props": [1, "consistent-as-needed"], + "no-cond-assign": [2, "except-parens"], + "radix": 0, + "space-infix-ops": 0, + "no-unused-vars": [1, {"vars": "local", "args": "none"}], + "default-case": 0, + "no-else-return": 0, + "no-param-reassign": 0, + "quotes": 0, + "comma-dangle": [1, "only-multiline"], + "vars-on-top": 0, + "prefer-const": 1, + "eqeqeq": 1, + "import/imports-first": 1, + "consistent-return": 0, + "import/no-mutable-exports": 0, + "no-class-assign": 0, + "jsx-a11y/label-has-for": 0, + "jsx-a11y/href-no-hash": 1, + "camelcase": 0, + "no-fallthrough": 0, + "no-restricted-syntax": 1, + "guard-for-in": 1, + "array-callback-return": 1, + "class-methods-use-this": 1, + "jsx-a11y/no-static-element-interactions": 1, + "react/forbid-prop-types": 1, + "arrow-parens": 1, + "no-bitwise": 1, + "no-plusplus": 1, + "no-extend-native": 1, + "no-use-before-define": 1, + "no-unused-expressions": 1, + "no-continue": 1, + "no-prototype-builtins": 1, + "dot-notation": 1, + "react/no-array-index-key": 1 + } } diff --git a/baselayer b/baselayer index bc2991f..26c7525 160000 --- a/baselayer +++ b/baselayer @@ -1 +1 @@ -Subproject commit bc2991f441a2caf61a057bae51065d7f8a137732 +Subproject commit 26c7525a4561f924d8b9c382dc970e734db840c0 diff --git a/package.json b/package.json index b5ce18e..331ba17 100644 --- a/package.json +++ b/package.json @@ -39,12 +39,12 @@ "babel-preset-stage-2": "^6.22.0", "css-loader": "^0.26.1", "cwise": "^1.0.9", - "eslint": "^3.16.1", - "eslint-config-airbnb": "^14.1.0", + "eslint": "^4.19.1", + "eslint-config-airbnb": "^16.1.0", "eslint-loader": "^1.6.3", - "eslint-plugin-import": "^2.2.0", - "eslint-plugin-jsx-a11y": "^4.0.0", - "eslint-plugin-react": "^6.10.0", + "eslint-plugin-import": "^2.11.0", + "eslint-plugin-jsx-a11y": "^6.0.3", + "eslint-plugin-react": "^7.7.0", "exports-loader": "^0.6.3", "glslify": "^6.0.1", "imports-loader": "^0.7.0", diff --git a/static/js/CesiumMessageHandler.js b/static/js/CesiumMessageHandler.js index f0fbef7..44f35b8 100644 --- a/static/js/CesiumMessageHandler.js +++ b/static/js/CesiumMessageHandler.js @@ -1,37 +1,36 @@ -import * as Action from './actions'; import { SHOW_NOTIFICATION, showNotification } from 'baselayer/components/Notifications'; import MessageHandler from 'baselayer/MessageHandler'; +import * as Action from './actions'; -let CesiumMessageHandler = dispatch => { - return new MessageHandler(dispatch, message => { - switch (message.action) { - case Action.FETCH_PROJECTS: - dispatch(Action.fetchProjects()); - break; - case Action.FETCH_FEATURES: - dispatch(Action.fetchFeatures()); - break; - case Action.FETCH_DATASETS: - dispatch(Action.fetchDatasets()); - break; - case Action.FETCH_FEATURESETS: - dispatch(Action.fetchFeaturesets()); - break; - case Action.FETCH_MODELS: - dispatch(Action.fetchModels()); - break; - case Action.FETCH_PREDICTIONS: - dispatch(Action.fetchPredictions()); - break; - case Action.FEATURIZE_PROGRESS: - let time_update = message.payload; - dispatch(Action.featurizeUpdateProgress(time_update)); - break; - default: - console.log('Unknown message received through flow:', - message); +const CesiumMessageHandler = dispatch => new MessageHandler(dispatch, (message) => { + switch (message.action) { + case Action.FETCH_PROJECTS: + dispatch(Action.fetchProjects()); + break; + case Action.FETCH_FEATURES: + dispatch(Action.fetchFeatures()); + break; + case Action.FETCH_DATASETS: + dispatch(Action.fetchDatasets()); + break; + case Action.FETCH_FEATURESETS: + dispatch(Action.fetchFeaturesets()); + break; + case Action.FETCH_MODELS: + dispatch(Action.fetchModels()); + break; + case Action.FETCH_PREDICTIONS: + dispatch(Action.fetchPredictions()); + break; + case Action.FEATURIZE_PROGRESS: { + const time_update = message.payload; + dispatch(Action.featurizeUpdateProgress(time_update)); + break; } - }); -} + default: + console.log('Unknown message received through flow:', + message); + } +}); export default CesiumMessageHandler; diff --git a/static/js/actions.js b/static/js/actions.js index 34b7f04..a1af2d8 100644 --- a/static/js/actions.js +++ b/static/js/actions.js @@ -3,6 +3,10 @@ import { reset as resetForm } from 'redux-form'; +import { showNotification } from 'baselayer/components/Notifications'; +import promiseAction from './action_tools'; + + export const HYDRATE = 'cesium/HYDRATE'; export const FETCH_PROJECTS = 'cesium/FETCH_PROJECTS'; @@ -51,15 +55,12 @@ export const RECEIVE_USER_PROFILE = 'cesium/FETCH_USER_PROFILE'; export const FEATURIZE_PROGRESS = 'cesium/FEATURIZE_PROGRESS'; -import { showNotification, reduceNotifications } from 'baselayer/components/Notifications'; -import promiseAction from './action_tools'; -import { objectType } from './utils'; // Refactor this into a utility function String.prototype.format = function (...args) { let i = 0; return this.replace(/{}/g, () => ( - typeof args[i] != 'undefined' ? args[i++] : '' + typeof args[i] !== 'undefined' ? args[i++] : '' )); }; @@ -85,18 +86,18 @@ export function fetchProjects() { }) .then(response => response.json()) .then((json) => { - if (json.status == 'success') { + if (json.status === 'success') { dispatch(receiveProjects(json.data)); } else { dispatch( showNotification( 'Error downloading projects ({})'.format(json.message) - )); + ) + ); } return json; - } - ).catch(ex => console.log('fetchProjects exception:', ex)) - ); + }).catch(ex => console.log('fetchProjects exception:', ex)) + ); } @@ -107,18 +108,20 @@ export function addProject(form) { dispatch, ADD_PROJECT, - fetch('/project', - { - credentials: 'same-origin', - method: 'POST', - body: JSON.stringify(form), - headers: new Headers({ - 'Content-Type': 'application/json' - }) - }) + fetch( + '/project', + { + credentials: 'same-origin', + method: 'POST', + body: JSON.stringify(form), + headers: new Headers({ + 'Content-Type': 'application/json' + }) + } + ) .then(response => response.json()) .then((json) => { - if (json.status == 'success') { + if (json.status === 'success') { dispatch(resetForm('newProject')); dispatch(showNotification('Added new project')); dispatch(selectProject(json.data.id)); @@ -127,7 +130,7 @@ export function addProject(form) { } return json; }) - ); + ); } @@ -138,18 +141,20 @@ export function updateProject(form) { dispatch, UPDATE_PROJECT, - fetch('/project/{}'.format(form.projectId), - { - credentials: 'same-origin', - method: 'PUT', - body: JSON.stringify(form), - headers: new Headers({ - 'Content-Type': 'application/json' - }) - }) + fetch( + '/project/{}'.format(form.projectId), + { + credentials: 'same-origin', + method: 'PUT', + body: JSON.stringify(form), + headers: new Headers({ + 'Content-Type': 'application/json' + }) + } + ) .then(response => response.json()) .then((json) => { - if (json.status == 'success') { + if (json.status === 'success') { dispatch(resetForm('newProject')); dispatch(showNotification('Successfully updated project')); } else { @@ -157,7 +162,7 @@ export function updateProject(form) { } return json; }) - ); + ); } @@ -173,35 +178,35 @@ export function deleteProject(id) { }) .then(response => response.json()) .then((json) => { - if (json.status == 'success') { + if (json.status === 'success') { dispatch(showNotification('Project deleted')); dispatch(selectProject()); } else { dispatch( showNotification( 'Error deleting project ({})'.format(json.message) - )); + ) + ); } }) - ); + ); } export function uploadDataset(form) { - - function fileReaderPromise(form, fileName, binary = false){ - return new Promise(resolve => { - var filereader = new FileReader(); + function fileReaderPromise(formFields, fileName, binary = false) { + return new Promise((resolve) => { + const filereader = new FileReader(); if (binary) { - filereader.readAsDataURL(form[fileName][0]); + filereader.readAsDataURL(formFields[fileName][0]); } else { - filereader.readAsText(form[fileName][0]); + filereader.readAsText(formFields[fileName][0]); } - filereader.onloadend = () => resolve({ body: filereader.result, - name: form[fileName][0].name }); + filereader.onloadend = () => resolve( + { body: filereader.result, name: formFields[fileName][0].name } + ); }); } - return dispatch => promiseAction( dispatch, @@ -209,31 +214,31 @@ export function uploadDataset(form) { Promise.all([fileReaderPromise(form, 'headerFile'), fileReaderPromise(form, 'tarFile', true)]) - .then(([headerData, tarData]) => { - form['headerFile'] = headerData; - form['tarFile'] = tarData; - - return fetch('/dataset', { - credentials: 'same-origin', - method: 'POST', - body: JSON.stringify(form), - headers: new Headers({ - 'Content-Type': 'application/json' - }) - }) - }) - .then(response => response.json()) - .then((json) => { - if (json.status == 'success') { - dispatch(showNotification('Successfully uploaded new dataset')); - dispatch(hideExpander('newDatasetExpander')); - dispatch(resetForm('newDataset')); - } else { - return Promise.reject({ _error: json.message }); - } - return json; - }) - ); + .then(([headerData, tarData]) => { + form.headerFile = headerData; + form.tarFile = tarData; + + return fetch('/dataset', { + credentials: 'same-origin', + method: 'POST', + body: JSON.stringify(form), + headers: new Headers({ + 'Content-Type': 'application/json' + }) + }); + }) + .then(response => response.json()) + .then((json) => { + if (json.status === 'success') { + dispatch(showNotification('Successfully uploaded new dataset')); + dispatch(hideExpander('newDatasetExpander')); + dispatch(resetForm('newDataset')); + } else { + return Promise.reject({ _error: json.message }); + } + return json; + }) + ); } // Download datasets @@ -247,10 +252,9 @@ export function fetchDatasets() { credentials: 'same-origin' }) .then(response => response.json()) - .then((json) => ( + .then(json => ( dispatch(receiveDatasets(json.data)) - ) - ).catch(ex => console.log('fetchDatasets', ex)) + )).catch(ex => console.log('fetchDatasets', ex)) ); } @@ -275,17 +279,17 @@ export function fetchFeaturesets() { }) .then(response => response.json()) .then((json) => { - if (json.status == 'success') { + if (json.status === 'success') { return dispatch(receiveFeaturesets(json.data)); } else { return dispatch( showNotification( 'Error downloading feature sets ({})'.format(json.message) - )); + ) + ); } - } - ).catch(ex => console.log('fetchFeaturesets', ex)) - ); + }).catch(ex => console.log('fetchFeaturesets', ex)) + ); } // Receive list of featuresets @@ -320,7 +324,7 @@ export function createModel(form) { }) .then(response => response.json()) .then((json) => { - if (json.status == 'success') { + if (json.status === 'success') { dispatch(resetForm('newModel')); dispatch(hideExpander('newModelExpander')); dispatch(showNotification('Model training begun.')); @@ -329,7 +333,7 @@ export function createModel(form) { } return json; }) - ); + ); } @@ -384,17 +388,17 @@ export function fetchFeatures() { }) .then(response => response.json()) .then((json) => { - if (json.status == 'success') { + if (json.status === 'success') { dispatch(receiveFeatures(json.data)); } else { dispatch( showNotification( 'Error downloading features ({})'.format(json.message) - )); + ) + ); } return json; - } - ).catch(ex => console.log('fetchFeatures exception:', ex)) + }).catch(ex => console.log('fetchFeatures exception:', ex)) ); } @@ -420,10 +424,8 @@ export function computeFeatures(form) { headers: new Headers({ 'Content-Type': 'application/json' }) - } - ).then(response => response.json() - ).then((json) => { - if (json.status == 'success') { + }).then(response => response.json()).then((json) => { + if (json.status === 'success') { dispatch(resetForm('featurize')); dispatch(showNotification('Feature computation begun.')); dispatch(hideExpander('featsetFormExpander')); @@ -448,16 +450,17 @@ export function deleteDataset(id) { }) .then(response => response.json()) .then((json) => { - if (json.status == 'success') { + if (json.status === 'success') { dispatch(showNotification('Dataset deleted')); } else { dispatch( showNotification( 'Error deleting dataset ({})'.format(json.message) - )); + ) + ); } }) - ); + ); } @@ -473,16 +476,17 @@ export function deleteFeatureset(id) { }) .then(response => response.json()) .then((json) => { - if (json.status == 'success') { + if (json.status === 'success') { dispatch(showNotification('Feature set deleted')); } else { dispatch( showNotification( 'Error deleting feature set ({})'.format(json.message) - )); + ) + ); } }) - ); + ); } @@ -497,18 +501,18 @@ export function fetchSklearnModels() { }) .then(response => response.json()) .then((json) => { - if (json.status == 'success') { + if (json.status === 'success') { dispatch(receiveSklearnModels(json.data)); } else { dispatch( showNotification( 'Error downloading sklearn models ({})'.format(json.message) - )); + ) + ); } return json; - } - ).catch(ex => console.log('fetchSklearnModels exception:', ex)) - ); + }).catch(ex => console.log('fetchSklearnModels exception:', ex)) + ); } function receiveSklearnModels(sklearn_models) { @@ -531,16 +535,16 @@ export function fetchModels() { }) .then(response => response.json()) .then((json) => { - if (json.status == 'success') { + if (json.status === 'success') { return dispatch(receiveModels(json.data)); } else { return dispatch( showNotification( 'Error downloading models ({})'.format(json.message) - )); + ) + ); } - } - ).catch(ex => console.log('fetchModels', ex)) + }).catch(ex => console.log('fetchModels', ex)) ); } @@ -565,16 +569,17 @@ export function deleteModel(id) { }) .then(response => response.json()) .then((json) => { - if (json.status == 'success') { + if (json.status === 'success') { dispatch(showNotification('Model deleted')); } else { dispatch( showNotification( 'Error deleting model ({})'.format(json.message) - )); + ) + ); } }) - ); + ); } @@ -591,10 +596,8 @@ export function doPrediction(form) { headers: new Headers({ 'Content-Type': 'application/json' }) - } - ).then(response => response.json() - ).then((json) => { - if (json.status == 'success') { + }).then(response => response.json()).then((json) => { + if (json.status === 'success') { dispatch(resetForm('predict')); dispatch(showNotification('Model predictions begun.')); dispatch(hideExpander('predictFormExpander')); @@ -619,13 +622,14 @@ export function deletePrediction(id) { }) .then(response => response.json()) .then((json) => { - if (json.status == 'success') { + if (json.status === 'success') { dispatch(showNotification('Prediction deleted')); } else { dispatch( showNotification( 'Error deleting prediction ({})'.format(json.message) - )); + ) + ); } }) ); @@ -644,13 +648,12 @@ export function fetchPredictions() { }) .then(response => response.json()) .then((json) => { - if (json.status == 'success') { + if (json.status === 'success') { return dispatch(receivePredictions(json.data)); } else { return dispatch(showNotification(json.message)); } - } - ).catch(ex => console.log('fetchPredictions', ex)) + }).catch(ex => console.log('fetchPredictions', ex)) ); } @@ -697,18 +700,18 @@ export function fetchUserProfile() { }) .then(response => response.json()) .then((json) => { - if (json.status == 'success') { + if (json.status === 'success') { dispatch(receiveUserProfile(json.data)); } else { dispatch( showNotification( 'Error downloading user profile ({})'.format(json.message) - )); + ) + ); } return json; - } - ).catch(ex => console.log('fetchUserProfile exception:', ex)) - ); + }).catch(ex => console.log('fetchUserProfile exception:', ex)) + ); } function receiveUserProfile(userProfile) { @@ -719,7 +722,6 @@ function receiveUserProfile(userProfile) { } - export function hydrate() { return (dispatch) => { dispatch(fetchProjects()) diff --git a/static/js/components/Datasets.jsx b/static/js/components/Datasets.jsx index 52fc781..0e4dee4 100644 --- a/static/js/components/Datasets.jsx +++ b/static/js/components/Datasets.jsx @@ -1,7 +1,9 @@ import React from 'react'; +import PropTypes from 'prop-types'; import { connect } from "react-redux"; import { reduxForm } from 'redux-form'; +import { showNotification } from 'baselayer/components/Notifications'; import { FormComponent, Form, TextInput, FileInput, SubmitButton } from './Form'; import * as Validate from '../validate'; import Expand from './Expand'; @@ -10,7 +12,6 @@ import * as Action from '../actions'; import { reformatDatetime } from '../utils'; import CesiumTooltip from './Tooltip'; import FoldableRow from './FoldableRow'; -import { showNotification } from 'baselayer/components/Notifications'; const DatasetsTab = props => ( @@ -25,12 +26,15 @@ const DatasetsTab = props => ( ); DatasetsTab.propTypes = { - selectedProject: React.PropTypes.object + selectedProject: PropTypes.object +}; +DatasetsTab.defaultProps = { + selectedProject: {} }; let DatasetForm = (props) => { const { fields: { datasetName, headerFile, tarFile }, - error, handleSubmit, submitting } = props; + error, handleSubmit, submitting } = props; const description = { fontStyle: 'italic', @@ -49,7 +53,7 @@ let DatasetForm = (props) => { />
- Format: comma-separated with columns "filename" (of a time series from the uploaded archive), "label" (class label or numerical value), and any metafeatures (numerical). + {'Format: comma-separated with columns "filename" (of a time series from the uploaded archive), "label" (class label or numerical value), and any metafeatures (numerical).'}
{ data-for="tarfileTooltip" />
- Format: zipfile or tarfile containing time series files, each of which is comma-separated with columns "time", "value", "error" (optional). + {'Format: zipfile or tarfile containing time series files, each of which is comma-separated with columns "time", "value", "error" (optional).'}
@@ -67,7 +71,7 @@ let DatasetForm = (props) => { , "ts1.dat,class_A",
, "..."]} + text={["filename,label",
, "ts1.dat,class_A",
, "..."]} /> { ); }; DatasetForm.propTypes = { - fields: React.PropTypes.object.isRequired, - error: React.PropTypes.string, - handleSubmit: React.PropTypes.func.isRequired, - submitting: React.PropTypes.bool.isRequired + fields: PropTypes.object.isRequired, + error: PropTypes.string, + handleSubmit: PropTypes.func.isRequired, + submitting: PropTypes.bool.isRequired +}; +DatasetForm.defaultProps = { + error: "" }; const dsMapStateToProps = (state, ownProps) => ( @@ -100,7 +107,7 @@ const dsMapStateToProps = (state, ownProps) => ( const dsMapDispatchToProps = (dispatch, ownProps) => ( { - onSubmit: form => { + onSubmit: (form) => { dispatch(showNotification('Dataset upload has begun.')); return dispatch(Action.uploadDataset(form)); } @@ -119,7 +126,7 @@ DatasetForm = reduxForm({ }, dsMapStateToProps, dsMapDispatchToProps)(DatasetForm); -let DatasetInfo = props => ( +const DatasetInfo = props => ( @@ -140,7 +147,7 @@ let DatasetInfo = props => (
); DatasetInfo.propTypes = { - dataset: React.PropTypes.object.isRequired + dataset: PropTypes.object.isRequired }; export let DatasetTable = props => ( @@ -177,7 +184,7 @@ export let DatasetTable = props => ( ); DatasetTable.propTypes = { - datasets: React.PropTypes.arrayOf(React.PropTypes.object) + datasets: PropTypes.arrayOf(PropTypes.object).isRequired }; diff --git a/static/js/components/Delete.jsx b/static/js/components/Delete.jsx index 7740cdd..23af35c 100644 --- a/static/js/components/Delete.jsx +++ b/static/js/components/Delete.jsx @@ -1,4 +1,5 @@ import React from 'react'; +import PropTypes from 'prop-types'; const Delete = (props) => { const style = { @@ -19,11 +20,14 @@ const Delete = (props) => { ); }; Delete.propTypes = { - ID: React.PropTypes.oneOfType([ - React.PropTypes.number, - React.PropTypes.string]).isRequired, - delete: React.PropTypes.func.isRequired, - typeName: React.PropTypes.string + ID: PropTypes.oneOfType([ + PropTypes.number, + PropTypes.string]).isRequired, + delete: PropTypes.func.isRequired, + typeName: PropTypes.string +}; +Delete.defaultProps = { + typeName: "" }; export default Delete; diff --git a/static/js/components/Dot.jsx b/static/js/components/Dot.jsx index f857df1..76e34dd 100644 --- a/static/js/components/Dot.jsx +++ b/static/js/components/Dot.jsx @@ -1,15 +1,15 @@ import React from 'react'; +import PropTypes from 'prop-types'; import colorScheme from './colorscheme'; const cs = colorScheme; const Dot = (props) => { - let value = props.value; + let { value, height } = { ...props }; if (value === undefined) { value = 'ยทยทยท'; } - let height = props.height; if (height === undefined) { height = '1em'; } @@ -37,9 +37,15 @@ const Dot = (props) => { ); }; Dot.propTypes = { - value: React.PropTypes.string.isRequired, - style: React.PropTypes.object, - height: React.PropTypes.string + /* eslint-disable react/no-unused-prop-types */ + value: PropTypes.string.isRequired, + style: PropTypes.object, + height: PropTypes.string + /* eslint-enable react/no-unused-prop-types */ +}; +Dot.defaultProps = { + style: {}, + height: "" }; export default Dot; diff --git a/static/js/components/Expand.jsx b/static/js/components/Expand.jsx index 58c1c40..4d84bd0 100644 --- a/static/js/components/Expand.jsx +++ b/static/js/components/Expand.jsx @@ -1,4 +1,5 @@ -import React, { Component, PropTypes } from 'react'; +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import { toggleExpander } from '../actions'; import Dot from './Dot'; @@ -61,13 +62,18 @@ let Expand = (props) => { Expand.propTypes = { expandBoxStyle: PropTypes.object, style: PropTypes.object, - label: PropTypes.string, - toggle: PropTypes.func, + label: PropTypes.string.isRequired, + toggle: PropTypes.func.isRequired, opened: PropTypes.bool, children: PropTypes.oneOfType([ PropTypes.arrayOf(PropTypes.oneOfType([ PropTypes.node, PropTypes.element])), - PropTypes.node, PropTypes.element]) + PropTypes.node, PropTypes.element]).isRequired +}; +Expand.defaultProps = { + expandBoxStyle: {}, + style: {}, + opened: false }; const mapStateToProps = (state, ownProps) => ( diff --git a/static/js/components/Features.jsx b/static/js/components/Features.jsx index 9bab7dd..4e61ce3 100644 --- a/static/js/components/Features.jsx +++ b/static/js/components/Features.jsx @@ -1,10 +1,11 @@ import React from 'react'; +import PropTypes from 'prop-types'; import { connect } from "react-redux"; import { reduxForm } from 'redux-form'; import ReactTabs from 'react-tabs'; import { FormComponent, Form, TextInput, TextareaInput, SubmitButton, - CheckBoxInput, SelectInput } from './Form'; + CheckBoxInput, SelectInput } from './Form'; import * as Validate from '../validate'; import Expand from './Expand'; import * as Action from '../actions'; @@ -13,16 +14,13 @@ import FoldableRow from './FoldableRow'; import { reformatDatetime, contains } from '../utils'; import Delete from './Delete'; -const Tab = ReactTabs.Tab; -const Tabs = ReactTabs.Tabs; -const TabList = ReactTabs.TabList; -const TabPanel = ReactTabs.TabPanel; +const { Tab, Tabs, TabList, TabPanel } = { ...ReactTabs }; let FeaturizeForm = (props) => { const { fields, fields: { datasetID, featuresetName, customFeatsCode }, - handleSubmit, submitting, resetForm, error, featuresList, - featureDescriptions } = props; + handleSubmit, submitting, resetForm, error, featuresList, + featureDescriptions } = props; const datasets = props.datasets.map(ds => ( { id: ds.id, label: ds.name } @@ -61,20 +59,22 @@ let FeaturizeForm = (props) => { { - Object.keys(props.featuresByCategory).map(ctgy => ( - {ctgy} + Object.keys(props.featuresByCategory).map((ctgy, idx) => ( + {ctgy} )) } Custom Features { - Object.keys(props.featuresByCategory).map(ctgy => ( - + Object.keys(props.featuresByCategory).map((ctgy, idx) => ( + { props.dispatch(Action.groupToggleCheckedFeatures( - props.featuresByCategory[ctgy])); }} + props.featuresByCategory[ctgy] +)); +}} > Check/Uncheck All @@ -83,8 +83,8 @@ let FeaturizeForm = (props) => { { props.featuresByCategory[ctgy].filter(feat => ( contains(featuresList, feat) - )).map((feature, idx) => ( - + )).map((feature, idx2) => ( + { @@ -116,17 +117,24 @@ let FeaturizeForm = (props) => { ); }; FeaturizeForm.propTypes = { - fields: React.PropTypes.object.isRequired, - datasets: React.PropTypes.arrayOf(React.PropTypes.object).isRequired, - error: React.PropTypes.string, - handleSubmit: React.PropTypes.func.isRequired, - submitting: React.PropTypes.bool.isRequired, - resetForm: React.PropTypes.func.isRequired, - selectedProject: React.PropTypes.object, - featuresByCategory: React.PropTypes.object, - tagList: React.PropTypes.arrayOf(React.PropTypes.string).isRequired, - featuresList: React.PropTypes.array, - featureDescriptions: React.PropTypes.object + fields: PropTypes.object.isRequired, + datasets: PropTypes.arrayOf(PropTypes.object).isRequired, + error: PropTypes.string, + handleSubmit: PropTypes.func.isRequired, + submitting: PropTypes.bool.isRequired, + resetForm: PropTypes.func.isRequired, + selectedProject: PropTypes.object, + featuresByCategory: PropTypes.object, + tagList: PropTypes.arrayOf(PropTypes.string).isRequired, + featuresList: PropTypes.array, + featureDescriptions: PropTypes.object +}; +FeaturizeForm.defaultProps = { + error: "", + selectedProject: {}, + featuresByCategory: {}, + featuresList: [], + featureDescriptions: {} }; @@ -147,10 +155,11 @@ const mapStateToProps = (state, ownProps) => { featureDescriptions: state.features.descriptions, datasets: filteredDatasets, fields: featuresList.concat( - ['datasetID', 'featuresetName', 'customFeatsCode']), + ['datasetID', 'featuresetName', 'customFeatsCode'] + ), initialValues: { ...initialValues, - datasetID: zerothDataset ? zerothDataset.id.toString() : "", - customFeatsCode: "" } + datasetID: zerothDataset ? zerothDataset.id.toString() : "", + customFeatsCode: "" } }; }; @@ -188,9 +197,12 @@ let FeaturesTab = (props) => { ); }; FeaturesTab.propTypes = { - featurePlotURL: React.PropTypes.string.isRequired, - computeFeatures: React.PropTypes.func.isRequired, - selectedProject: React.PropTypes.object + featurePlotURL: PropTypes.string.isRequired, + computeFeatures: PropTypes.func.isRequired, + selectedProject: PropTypes.object +}; +FeaturesTab.defaultProps = { + selectedProject: {} }; const ftMapDispatchToProps = dispatch => ( @@ -224,7 +236,8 @@ export let FeatureTable = props => ( ); - let elapsed = "", percent = ""; + let elapsed = ""; + let percent = ""; if (featureset.progress) { ({ elapsed, percent } = { ...featureset.progress }); } @@ -248,15 +261,19 @@ export let FeatureTable = props => ( {foldedContent} - ); }) + ); +}) } ); FeatureTable.propTypes = { - featuresets: React.PropTypes.arrayOf(React.PropTypes.object), - featurePlotURL: React.PropTypes.string + featuresets: PropTypes.arrayOf(PropTypes.object).isRequired, + featurePlotURL: PropTypes.string +}; +FeatureTable.defaultProps = { + featurePlotURL: null }; diff --git a/static/js/components/FoldableRow.jsx b/static/js/components/FoldableRow.jsx index ab71121..f081d26 100644 --- a/static/js/components/FoldableRow.jsx +++ b/static/js/components/FoldableRow.jsx @@ -53,7 +53,8 @@ class FoldableRow extends Component { e, { style: this.state.folded ? {} : openStyleContent, key: idx - }) + } + ) )); return ( @@ -70,5 +71,8 @@ FoldableRow.propTypes = { PropTypes.node, PropTypes.element])), PropTypes.node, PropTypes.element]) }; +FoldableRow.defaultProps = { + children: [] +}; export default FoldableRow; diff --git a/static/js/components/Form.jsx b/static/js/components/Form.jsx index 56e3e27..69a5271 100644 --- a/static/js/components/Form.jsx +++ b/static/js/components/Form.jsx @@ -1,4 +1,6 @@ -import React, { PropTypes } from 'react'; +import React from 'react'; +import PropTypes from 'prop-types'; + export const FormComponent = (props) => { }; @@ -28,6 +30,10 @@ Error.propTypes = { error: PropTypes.string, touched: PropTypes.bool }; +Error.defaultProps = { + error: "", + touched: false +}; export const Form = (props) => { const style = { @@ -57,7 +63,10 @@ Form.propTypes = { PropTypes.element, PropTypes.arrayOf(PropTypes.element), PropTypes.arrayOf(PropTypes.node) - ]) + ]).isRequired +}; +Form.defaultProps = { + error: "" }; export const TextInput = (props) => { @@ -87,8 +96,8 @@ export const TextInput = (props) => { }; TextInput.propTypes = { - label: PropTypes.string, - value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]) + label: PropTypes.string.isRequired, + value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired }; export const TextareaInput = (props) => { @@ -116,8 +125,8 @@ export const TextareaInput = (props) => { ); }; TextareaInput.propTypes = { - label: PropTypes.string, - value: PropTypes.string + label: PropTypes.string.isRequired, + value: PropTypes.string.isRequired }; export const CheckBoxInput = (props) => { @@ -138,9 +147,12 @@ export const CheckBoxInput = (props) => { ); }; CheckBoxInput.propTypes = { - label: PropTypes.string, + label: PropTypes.string.isRequired, divStyle: PropTypes.object }; +CheckBoxInput.defaultProps = { + divStyle: {} +}; export const SelectInput = (props) => { const selectInputStyle = { @@ -186,6 +198,10 @@ SelectInput.propTypes = { })) /* eslint-enable react/no-unused-prop-types */ }; +SelectInput.defaultProps = { + label: "", + options: [] +}; export const SubmitButton = (props) => { @@ -209,7 +225,10 @@ SubmitButton.propTypes = { label: PropTypes.string, disabled: PropTypes.bool }; - +SubmitButton.defaultProps = { + label: "", + disabled: false +}; export const FileInput = (props) => { const fileInputStyle = { @@ -233,9 +252,9 @@ export const FileInput = (props) => { ); }; FileInput.propTypes = { - label: PropTypes.string, + label: PropTypes.string.isRequired, value: PropTypes.oneOfType([ PropTypes.string, PropTypes.object - ]) + ]).isRequired }; diff --git a/static/js/components/Main.jsx b/static/js/components/Main.jsx index 54a1d4f..383b443 100644 --- a/static/js/components/Main.jsx +++ b/static/js/components/Main.jsx @@ -1,4 +1,5 @@ import React from 'react'; +import PropTypes from 'prop-types'; import { connect, Provider } from 'react-redux'; import ReactDOM from 'react-dom'; import ReactTabs from 'react-tabs'; @@ -6,25 +7,22 @@ import ReactTabs from 'react-tabs'; import 'bootstrap-css'; import 'bootstrap'; +import { Notifications } from 'baselayer/components/Notifications'; +import WebSocket from 'baselayer/components/WebSocket'; import configureStore from '../configureStore'; import * as Action from '../actions'; -import WebSocket from 'baselayer/components/WebSocket'; import CesiumMessageHandler from '../CesiumMessageHandler'; import { ProjectSelector, AddProject, ProjectTab } from './Projects'; import DatasetsTab from './Datasets'; import FeaturesTab from './Features'; import ModelsTab from './Models'; import PredictTab from './Predictions'; -import { Notifications } from 'baselayer/components/Notifications'; import colorScheme from './colorscheme'; import Progress from './Progress'; import CesiumTooltip from './Tooltip'; import UserProfile from './UserProfile'; -const Tab = ReactTabs.Tab; -const Tabs = ReactTabs.Tabs; -const TabList = ReactTabs.TabList; -const TabPanel = ReactTabs.TabPanel; +const { Tab, Tabs, TabList, TabPanel } = { ...ReactTabs }; const cs = colorScheme; const store = configureStore(); @@ -222,7 +220,7 @@ class MainContent extends React.Component { /> - +
@@ -299,7 +297,7 @@ class MainContent extends React.Component { > @@ -315,7 +313,7 @@ class MainContent extends React.Component { @@ -372,25 +370,30 @@ class MainContent extends React.Component { } } MainContent.propTypes = { - selectedProject: React.PropTypes.object.isRequired, - root: React.PropTypes.string.isRequired, - logoSpinAngle: React.PropTypes.number.isRequired, - spinLogo: React.PropTypes.func + selectedProject: PropTypes.object, + root: PropTypes.string.isRequired, + logoSpinAngle: PropTypes.number.isRequired, + spinLogo: PropTypes.func.isRequired, + username: PropTypes.string +}; +MainContent.defaultProps = { + username: "", + selectedProject: {} }; const mapStateToProps = function (state) { // This can be improved by using // http://redux-form.com/6.0.0-alpha.13/docs/api/FormValueSelector.md/ - const projectSelector = state.form.projectSelector; + const { projectSelector } = { ...state.form }; const selectedProjectId = projectSelector ? projectSelector.project.value : ""; let selectedProject = state.projects.projectList.filter( p => (p.id == selectedProjectId) ); - const firstProject = state.projects.projectList[0] || { id: '', label: '', description: '' }; + const [firstProject] = state.projects.projectList || { id: '', label: '', description: '' }; if (selectedProject.length > 0) { - selectedProject = selectedProject[0]; + [selectedProject] = selectedProject; } else { selectedProject = firstProject; } @@ -420,7 +423,7 @@ MainContent = connect(mapStateToProps, mapDispatchToProps)(MainContent); ReactDOM.render( - + , document.getElementById('content') ); diff --git a/static/js/components/Models.jsx b/static/js/components/Models.jsx index 1422cb8..e76e47e 100644 --- a/static/js/components/Models.jsx +++ b/static/js/components/Models.jsx @@ -1,4 +1,5 @@ -import React, { PropTypes } from 'react'; +import React from 'react'; +import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import { reduxForm } from 'redux-form'; @@ -23,13 +24,16 @@ const ModelsTab = props => (
); ModelsTab.propTypes = { - selectedProject: React.PropTypes.object + selectedProject: PropTypes.object +}; +ModelsTab.defaultProps = { + selectedProject: null }; let NewModelForm = (props) => { const { fields, - fields: { modelName, featureset, modelType }, - error, handleSubmit } = props; + fields: { modelName, featureset, modelType }, + error, handleSubmit } = props; const skModels = props.models; const selectModels = []; @@ -45,13 +49,13 @@ let NewModelForm = (props) => { } const featuresets = props.featuresets - .filter(fs => !Validate.isEmpty(fs.finished)) - .map(fs => ( - { - id: fs.id, - label: fs.name - } - )); + .filter(fs => !Validate.isEmpty(fs.finished)) + .map(fs => ( + { + id: fs.id, + label: fs.name + } + )); const chosenModel = props.models[modelType.value]; @@ -62,12 +66,14 @@ let NewModelForm = (props) => { @@ -79,11 +85,16 @@ let NewModelForm = (props) => { ); }; NewModelForm.propTypes = { - fields: React.PropTypes.object.isRequired, - error: React.PropTypes.string, - handleSubmit: React.PropTypes.func.isRequired, - featuresets: React.PropTypes.arrayOf(React.PropTypes.object).isRequired, - models: React.PropTypes.object.isRequired + fields: PropTypes.object.isRequired, + error: PropTypes.string, + handleSubmit: PropTypes.func.isRequired, + featuresets: PropTypes.arrayOf(PropTypes.object).isRequired, + models: PropTypes.object.isRequired, + selectedProject: PropTypes.object +}; +NewModelForm.defaultProps = { + error: null, + selectedProject: null }; const mapStateToProps = function (state, ownProps) { @@ -142,12 +153,12 @@ export const Model = (props) => { const style = { }; - const model = props.model; + const { model } = { ...props }; return (

{model.name}

- {model.params.map((param, idx) => { + {model.params.map((param, idx) => { const pProps = props[param.name]; if (param.type === 'bool') { return ; @@ -158,12 +169,9 @@ export const Model = (props) => {
); }; -Model.propTypes = { - model: React.PropTypes.object.isRequired -}; -let ModelInfo = props => ( +const ModelInfo = props => ( @@ -181,8 +189,8 @@ let ModelInfo = props => (
{ - Object.keys(props.model.params).map(param => ( - + Object.keys(props.model.params).map((param, idx) => ( + @@ -199,7 +207,7 @@ let ModelInfo = props => (
{param} {JSON.stringify(props.model.params[param])}
); ModelInfo.propTypes = { - model: React.PropTypes.object.isRequired + model: PropTypes.object.isRequired }; export let ModelTable = props => ( @@ -242,11 +250,15 @@ export let ModelTable = props => ( ModelTable.propTypes = { models: PropTypes.arrayOf(PropTypes.object) }; +ModelTable.defaultProps = { + models: null +}; const mtMapStateToProps = (state, ownProps) => ( { models: state.models.filter( - model => (model.project_id === ownProps.selectedProject.id)) + model => (model.project_id === ownProps.selectedProject.id) + ) } ); diff --git a/static/js/components/Plot.jsx b/static/js/components/Plot.jsx index f1bf02e..3d2335e 100644 --- a/static/js/components/Plot.jsx +++ b/static/js/components/Plot.jsx @@ -1,23 +1,28 @@ import React, { Component } from 'react'; import { connect } from 'react-redux'; import { showNotification } from 'baselayer/components/Notifications'; +/* eslint-disable */ import "../../../node_modules/bokehjs/build/js/bokeh.js"; import "../../../node_modules/bokehjs/build/css/bokeh.css"; +/* eslint-enable */ + function bokeh_render_plot(node, docs_json, render_items) { // Create bokeh div element - var bokeh_div = document.createElement("div"); - var inner_div = document.createElement("div"); - bokeh_div.setAttribute("class", "bk-root" ); + const bokeh_div = document.createElement("div"); + const inner_div = document.createElement("div"); + bokeh_div.setAttribute("class", "bk-root"); inner_div.setAttribute("class", "bk-plotdiv"); inner_div.setAttribute("id", render_items[0].elementid); bokeh_div.appendChild(inner_div); node.appendChild(bokeh_div); // Generate plot - Bokeh.safely(function() { + /* eslint-disable */ + Bokeh.safely(() => { Bokeh.embed.embed_items(docs_json, render_items); }); + /* eslint-enable */ } class Plot extends Component { @@ -50,15 +55,15 @@ class Plot extends Component { if (!plotData) { return Please wait while we load your plotting data...; } - var docs_json = JSON.parse(plotData.docs_json); - var render_items = JSON.parse(plotData.render_items); + const docs_json = JSON.parse(plotData.docs_json); + const render_items = JSON.parse(plotData.render_items); return ( plotData &&
{ - node && bokeh_render_plot(node, docs_json, render_items) + node && bokeh_render_plot(node, docs_json, render_items); } } /> diff --git a/static/js/components/Predictions.jsx b/static/js/components/Predictions.jsx index c574259..dd1245d 100644 --- a/static/js/components/Predictions.jsx +++ b/static/js/components/Predictions.jsx @@ -1,9 +1,10 @@ -import React, { PropTypes } from 'react'; +import React from 'react'; +import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import { reduxForm } from 'redux-form'; import { FormComponent, SelectInput, SubmitButton, - Form } from './Form'; + Form } from './Form'; import * as Validate from '../validate'; @@ -16,7 +17,7 @@ import Delete from './Delete'; let PredictForm = (props) => { const { fields: { modelID, datasetID }, handleSubmit, submitting, resetForm, - error } = props; + error } = props; const datasets = props.datasets.map(ds => ( { id: ds.id, @@ -24,11 +25,11 @@ let PredictForm = (props) => { )); const models = props.models - .filter(model => !Validate.isEmpty(model.finished)) - .map(model => ( - { id: model.id, - label: model.name } - )); + .filter(model => !Validate.isEmpty(model.finished)) + .map(model => ( + { id: model.id, + label: model.name } + )); return (
@@ -55,14 +56,17 @@ let PredictForm = (props) => { ); }; PredictForm.propTypes = { - fields: React.PropTypes.object.isRequired, - error: React.PropTypes.string, - handleSubmit: React.PropTypes.func.isRequired, - resetForm: React.PropTypes.func.isRequired, - submitting: React.PropTypes.bool.isRequired, - datasets: React.PropTypes.arrayOf(React.PropTypes.object).isRequired, - models: React.PropTypes.arrayOf(React.PropTypes.object).isRequired, - selectedProject: React.PropTypes.object.isRequired + fields: PropTypes.object.isRequired, + error: PropTypes.string, + handleSubmit: PropTypes.func.isRequired, + resetForm: PropTypes.func.isRequired, + submitting: PropTypes.bool.isRequired, + datasets: PropTypes.arrayOf(PropTypes.object).isRequired, + models: PropTypes.arrayOf(PropTypes.object).isRequired, + selectedProject: PropTypes.object.isRequired +}; +PredictForm.defaultProps = { + error: null }; @@ -80,7 +84,7 @@ const mapStateToProps = (state, ownProps) => { models: filteredModels, fields: ['modelID', 'datasetID'], initialValues: { modelID: zerothModel ? zerothModel.id : '', - datasetID: zerothDataset ? zerothDataset.id : '' } + datasetID: zerothDataset ? zerothDataset.id : '' } }; }; @@ -139,7 +143,8 @@ let PredictionsTable = props => ( {foldedContent} - ); }) + ); +}) } @@ -147,25 +152,26 @@ let PredictionsTable = props => ( PredictionsTable.propTypes = { predictions: PropTypes.arrayOf(PropTypes.object) }; +PredictionsTable.defaultProps = { + predictions: null +}; const PredictionResults = (props) => { - const modelType = props.prediction.model_type; - const results = props.prediction.results; + const { model_type, results, isProbabilistic } = { ...props.prediction }; const firstResult = results ? results[Object.keys(results)[0]] : null; const classes = (firstResult && firstResult.prediction) ? - Object.keys(firstResult.prediction) : null; + Object.keys(firstResult.prediction) : null; - let modelHasClass = contains(['RidgeClassifierCV'], modelType); - const modelHasProba = props.prediction.isProbabilistic; + let modelHasClass = contains(['RidgeClassifierCV'], model_type); const modelHasTarget = contains(['RandomForestRegressor', - 'LinearRegressor', - 'BayesianARDRegressor', - 'BayesianRidgeRegressor'], - modelType); - if (modelType === 'LinearSGDClassifier') { - modelHasClass = !modelHasProba; + 'LinearRegressor', + 'BayesianARDRegressor', + 'BayesianRidgeRegressor'], + model_type); + if (model_type === 'LinearSGDClassifier') { + modelHasClass = !isProbabilistic; } const hasTrueTargetLabel = p => (p && p.label); @@ -177,7 +183,7 @@ const PredictionResults = (props) => { Time Series {hasTrueTargetLabel(firstResult) && True Class/Target} - {modelHasProba && + {isProbabilistic && classes.map((classLabel, idx) => ([ Predicted Class, Probability @@ -190,7 +196,7 @@ const PredictionResults = (props) => { - {results && Object.keys(results).map((fname, idx) => { + {results && Object.keys(results).map((fname, idx) => { const result = results[fname]; const classesSorted = classes.sort((a, b) => (result.prediction[b] - result.prediction[a])); @@ -203,7 +209,7 @@ const PredictionResults = (props) => { [hasTrueTargetLabel(result) && {result.label}, - modelHasProba && + isProbabilistic && classesSorted.map((classLabel, idx2) => ([ {classLabel}, {result.prediction[classLabel]} @@ -215,7 +221,8 @@ const PredictionResults = (props) => { ]} - ); })} + ); +})} ); @@ -236,12 +243,12 @@ const ptMapStateToProps = (state, ownProps) => { PredictionsTable = connect(ptMapStateToProps)(PredictionsTable); const dpMapDispatchToProps = dispatch => ( - { delete: id => dispatch(Action.deletePrediction(id)) } + { delete: id => dispatch(Action.deletePrediction(id)) } ); const DeletePrediction = connect(null, dpMapDispatchToProps)(Delete); -const DownloadPredCSV = (props) => ( +const DownloadPredCSV = props => ( ( diff --git a/static/js/components/Projects.jsx b/static/js/components/Projects.jsx index b1f4988..8398a0d 100644 --- a/static/js/components/Projects.jsx +++ b/static/js/components/Projects.jsx @@ -13,7 +13,7 @@ const cs = colorScheme; const ProjectForm = (props) => { const { fields: { projectName, projectDescription }, - error, resetForm, submitting, handleSubmit } = props; + error, resetForm, submitting, handleSubmit } = props; return (
@@ -21,18 +21,22 @@ const ProjectForm = (props) => { ); }; ProjectForm.propTypes = { - fields: React.PropTypes.object.isRequired, - label: React.PropTypes.string.isRequired, - error: React.PropTypes.string, - handleSubmit: React.PropTypes.func.isRequired, - submitting: React.PropTypes.bool.isRequired, - resetForm: React.PropTypes.func.isRequired + fields: PropTypes.object.isRequired, + label: PropTypes.string.isRequired, + error: PropTypes.string, + handleSubmit: PropTypes.func.isRequired, + submitting: PropTypes.bool.isRequired, + resetForm: PropTypes.func.isRequired +}; +ProjectForm.defaultProps = { + error: null }; const validate = Validate.createValidator({ @@ -123,7 +127,8 @@ let AddProject = (props) => { @@ -136,6 +141,10 @@ AddProject.propTypes = { addProject: PropTypes.func.isRequired, style: PropTypes.object }; +AddProject.defaultProps = { + label: "", + style: {} +}; let mapDispatchToProps = dispatch => ( { @@ -176,11 +185,15 @@ let ProjectSelector = (props) => { ); }; ProjectSelector.propTypes = { - fields: PropTypes.object, + fields: PropTypes.object.isRequired, projects: PropTypes.arrayOf(PropTypes.object).isRequired, style: PropTypes.object, label: PropTypes.string }; +ProjectSelector.defaultProps = { + style: {}, + label: "" +}; const psMapStateToProps = (state) => { const projectZero = state.projects.projectList[0]; @@ -221,5 +234,5 @@ export const CurrentProject = (props) => { }; CurrentProject.propTypes = { - selectedProject: PropTypes.object + selectedProject: PropTypes.object.isRequired }; diff --git a/static/js/components/Tooltip.jsx b/static/js/components/Tooltip.jsx index 01480e0..bbf0a61 100644 --- a/static/js/components/Tooltip.jsx +++ b/static/js/components/Tooltip.jsx @@ -1,4 +1,5 @@ import React from 'react'; +import PropTypes from 'prop-types'; import ReactTooltip from 'react-tooltip'; const CesiumTooltip = props => ( @@ -9,13 +10,13 @@ const CesiumTooltip = props => ( ); CesiumTooltip.propTypes = { - id: React.PropTypes.string.isRequired, - text: React.PropTypes.oneOfType([ - React.PropTypes.string, - React.PropTypes.array + id: PropTypes.string.isRequired, + text: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.array ]).isRequired, - place: React.PropTypes.string, - delay: React.PropTypes.number + place: PropTypes.string, + delay: PropTypes.number }; CesiumTooltip.defaultProps = { place: 'top', diff --git a/static/js/components/UserProfile.jsx b/static/js/components/UserProfile.jsx index 3a5f5c0..36d67c6 100644 --- a/static/js/components/UserProfile.jsx +++ b/static/js/components/UserProfile.jsx @@ -1,10 +1,12 @@ import React from 'react'; import { connect } from 'react-redux'; -let UserProfile = (props) => { - return ( -
{props.profile.username}
- ); +let UserProfile = props => ( +
{props.profile.username}
+); +UserProfile.propTypes = { + style: React.PropTypes.object.isRequired, + profile: React.PropTypes.object.isRequired }; const mapStateToProps = state => ( diff --git a/static/js/reducers.js b/static/js/reducers.js index 0e022f8..052e043 100644 --- a/static/js/reducers.js +++ b/static/js/reducers.js @@ -1,8 +1,8 @@ import { combineReducers } from 'redux'; import { reducer as formReducer } from 'redux-form'; -import * as Action from './actions'; import { reducer as notifications } from 'baselayer/components/Notifications'; +import * as Action from './actions'; import { contains, joinObjectValues } from './utils'; @@ -40,20 +40,21 @@ function featuresets(state=[], action) { switch (action.type) { case Action.RECEIVE_FEATURESETS: return action.payload; - case Action.FEATURIZE_PROGRESS: - const newState = [ ...state ]; + case Action.FEATURIZE_PROGRESS: { + const newState = [...state]; const { percent, elapsed } = { ...action.payload }; - const featureIdx = newState.findIndex((element) => ( - element.id == action.payload.fsetID + const featureIdx = newState.findIndex(element => ( + element.id === action.payload.fsetID )); - if (featureIdx != -1) { + if (featureIdx !== -1) { const feature = newState[featureIdx]; feature.progress = { percent, elapsed }; } return newState; + } default: return state; } @@ -68,13 +69,13 @@ function features(state={}, action) { const allFeatsList = joinObjectValues(action.payload.features_by_category); return { ...action.payload, - allFeatsList, - tagList, - checkedTags: tagList.slice(0), - featsWithCheckedTags: allFeatsList.slice(0) }; + allFeatsList, + tagList, + checkedTags: tagList.slice(0), + featsWithCheckedTags: allFeatsList.slice(0) }; } case Action.CLICK_FEATURE_TAG_CHECKBOX: { - let checkedTags = state.checkedTags.slice(0); + const checkedTags = state.checkedTags.slice(0); if (checkedTags.indexOf(action.payload.tag) > -1) { checkedTags.splice(checkedTags.indexOf(action.payload.tag), 1); @@ -85,8 +86,8 @@ function features(state={}, action) { const featsWithCheckedTags = state.allFeatsList.filter(feature => ( state.tags[feature].some(tag => contains(checkedTags, tag)))); return { ...state, - featsWithCheckedTags, - checkedTags }; + featsWithCheckedTags, + checkedTags }; } default: return state; @@ -124,10 +125,12 @@ const myFormReducer = theirFormReducer => ( } case Action.GROUP_TOGGLE_FEATURES: { const field_names = Object.keys(state.featurize).filter( - field_name => contains(action.payload.ctgy_list, field_name)); + field_name => contains(action.payload.ctgy_list, field_name) + ); const featurizeFormState = Object.assign({}, state.featurize); const allAreChecked = (field_names.filter( - el => !featurizeFormState[el].value).length === 0); + el => !featurizeFormState[el].value + ).length === 0); for (const idx in field_names) { featurizeFormState[field_names[idx]].value = !allAreChecked; } diff --git a/static/js/validate.js b/static/js/validate.js index c283672..87f3969 100644 --- a/static/js/validate.js +++ b/static/js/validate.js @@ -7,8 +7,6 @@ export function email(value) { // Let's not start a debate on email regex. This is just for an example app! if (!isEmpty(value) && !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(value)) { return 'Invalid email address'; - } else { - return; } } From b3827575197ef87cc3d3382fe6baefbfd79539d7 Mon Sep 17 00:00:00 2001 From: Ari Crellin-Quick Date: Thu, 19 Apr 2018 18:31:18 -0700 Subject: [PATCH 3/3] Update baselayer --- baselayer | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/baselayer b/baselayer index 26c7525..4b64f33 160000 --- a/baselayer +++ b/baselayer @@ -1 +1 @@ -Subproject commit 26c7525a4561f924d8b9c382dc970e734db840c0 +Subproject commit 4b64f339d9d450119991d8f00c546b1e3dbb32ee