diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000000000000000000000000000000000000..8cb839b77718e11cf14b047af0141eccf647f5c6 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,26 @@ +{ + "extends": "airbnb", + "plugins": [ + "react", + "jsx-a11y", + "import" + ], + "rules": { + "react/jsx-filename-extension": [1, { + "extensions": [ + ".js" + ]} + ], + "comma-dangle": ["error", { + "arrays": "always-multiline", + "objects": "always-multiline", + "imports": "always-multiline", + "exports": "always-multiline", + "functions": "ignore" + }] + }, + "env": { + "browser": true, + "jest": true + } +} \ No newline at end of file diff --git a/package.json b/package.json index 7e9a9d208d4cf7597ede251d4a701d89f28157c8..68c221d5a588b6be5c2c9d348b2028bf1a33eed0 100644 --- a/package.json +++ b/package.json @@ -16,13 +16,14 @@ "css-loader": "0.25.0", "detect-port": "1.0.1", "dotenv": "2.0.0", - "eslint": "3.8.1", - "eslint-config-react-app": "^0.3.0", + "eslint": "3.9.1", + "eslint-config-airbnb": "^13.0.0", + "eslint-config-react-app": "^0.5.0", "eslint-loader": "1.6.0", "eslint-plugin-flowtype": "2.21.0", - "eslint-plugin-import": "2.0.1", + "eslint-plugin-import": "2.1.0", "eslint-plugin-jsx-a11y": "2.2.3", - "eslint-plugin-react": "6.4.1", + "eslint-plugin-react": "6.6.0", "extract-text-webpack-plugin": "1.0.1", "file-loader": "0.9.0", "filesize": "3.3.0", @@ -52,6 +53,8 @@ "react": "^15.3.2", "react-dom": "^15.3.2", "react-redux": "^4.4.5", + "react-router": "^3.0.0", + "react-router-redux": "^4.0.7", "redux": "^3.6.0", "redux-promise": "^0.5.3", "whatwg-fetch": "^1.0.0" @@ -83,8 +86,5 @@ "presets": [ "react-app" ] - }, - "eslintConfig": { - "extends": "react-app" } } diff --git a/src/components/BuildingList/index.js b/src/components/BuildingList/index.js deleted file mode 100644 index 8de12843a4e42f779de82707e11303d06547e60b..0000000000000000000000000000000000000000 --- a/src/components/BuildingList/index.js +++ /dev/null @@ -1,34 +0,0 @@ -import React from 'react'; - -import BuildingListItem from '../BuildingListItem/index' -import './styles.css'; - -export default function BuildingList({buildings}) { - if (!buildings || buildings.length === 0) { - return

Loading...

- } - - let i = 0; - const buildingProperties = Object.keys(buildings[0]).map((item) => { - return { item } - }); - - const buildingItems = buildings.map((building) => { - return ( - - ) - }); - - return ( - - - - {buildingProperties} - - - - {buildingItems} - -
- ) -}; diff --git a/src/components/BuildingList/styles.css b/src/components/BuildingList/styles.css deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/components/BuildingListItem/index.js b/src/components/BuildingListItem/index.js deleted file mode 100644 index b51cacbeffced6d697f430a1dd824e4bc892863e..0000000000000000000000000000000000000000 --- a/src/components/BuildingListItem/index.js +++ /dev/null @@ -1,16 +0,0 @@ -import React from 'react'; -import './styles.css'; - -export default function BuildingListItem(props) { - - let i = 0; - const building_items = Object.values(props).map((building_item) => { - return {building_item} - }); - - return ( - - {building_items} - - ) -}; diff --git a/src/components/BuildingListTable/index.js b/src/components/BuildingListTable/index.js new file mode 100644 index 0000000000000000000000000000000000000000..548cf6bc2a26c339fc39a0f4bf3b2bb5825fee0d --- /dev/null +++ b/src/components/BuildingListTable/index.js @@ -0,0 +1,47 @@ +import React, { PropTypes } from 'react'; +import './styles.css'; + +export default function BuildingListTable({ buildings }) { + if (!buildings || buildings.length === 0) { + return
None
; + } + + const buildingProperties = Object.keys(buildings[0]).map(item => + { item } , + ); + + const buildingItems = buildings.map(building => + ( + + {building.address} + {building.bbl} + {building.blocpower_id} + {building.borough} + {building.zipcode} + + ), + ); + + return ( + + + + {buildingProperties} + + + + {buildingItems} + +
+ ); +} + +BuildingListTable.propTypes = { + buildings: PropTypes.arrayOf(PropTypes.shape({ + address: PropTypes.state, + bbl: PropTypes.number, + blocpower_id: PropTypes.number, + borough: PropTypes.string, + zipcode: PropTypes.number, + })), +}; diff --git a/src/components/BuildingListItem/styles.css b/src/components/BuildingListTable/styles.css similarity index 100% rename from src/components/BuildingListItem/styles.css rename to src/components/BuildingListTable/styles.css diff --git a/src/containers/App/index.js b/src/containers/App/index.js deleted file mode 100644 index b725bd1575de485f43d03d51d81fb8e0ea45da33..0000000000000000000000000000000000000000 --- a/src/containers/App/index.js +++ /dev/null @@ -1,16 +0,0 @@ -import React, {Component} from 'react'; -import './styles.css'; - -import HomePage from '../HomePage'; - -class App extends Component { - render() { - return ( -
- -
- ); - } -} - -export default App; diff --git a/src/containers/App/index.test.js b/src/containers/App/index.test.js deleted file mode 100644 index ba444eb1b7b6c00e8380b5af03279eec67f28927..0000000000000000000000000000000000000000 --- a/src/containers/App/index.test.js +++ /dev/null @@ -1,8 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import App from './index'; - -it('renders without crashing', () => { - const div = document.createElement('div'); - ReactDOM.render(, div); -}); diff --git a/src/containers/App/styles.css b/src/containers/App/styles.css deleted file mode 100644 index 34b8ee09c60b8cfcc53807a2354c67d41d340899..0000000000000000000000000000000000000000 --- a/src/containers/App/styles.css +++ /dev/null @@ -1,3 +0,0 @@ -.App { - -} diff --git a/src/containers/BuildingList/actions.js b/src/containers/BuildingList/actions.js new file mode 100644 index 0000000000000000000000000000000000000000..ba423ec45cd026806c3df96e84de50a4b4749e12 --- /dev/null +++ b/src/containers/BuildingList/actions.js @@ -0,0 +1,32 @@ +import 'whatwg-fetch'; +import { FETCH_BUILDINGS } from './constants'; + +const ROOT_URL = `${process.env.REACT_APP_BUILDING_SERVICE}/building/`; +const myHeaders = new Headers({ 'x-blocpower-app-key': process.env.REACT_APP_KEY }); + +function fetchBuildings(address) { + const myInit = { + method: 'GET', + headers: myHeaders, + mode: 'cors', + cache: 'default', + }; + + const url = `${ROOT_URL}?address=${address}`; + + const request = fetch(url, myInit).then(response => + response.json() + ); + // .catch(function (error) { + // console.log('request failed for buildings', error) + // }); + + return { + type: FETCH_BUILDINGS, + payload: request, + }; +} + +/* eslint-disable import/prefer-default-export */ +export { fetchBuildings }; +/* eslint-enable */ diff --git a/src/containers/BuildingList/constants.js b/src/containers/BuildingList/constants.js new file mode 100644 index 0000000000000000000000000000000000000000..0cb538df646d5aab40bbd2b2c04bc40b3f43b325 --- /dev/null +++ b/src/containers/BuildingList/constants.js @@ -0,0 +1,3 @@ +/* eslint-disable import/prefer-default-export */ +export const FETCH_BUILDINGS = 'FETCH_BUILDINGS'; +/* eslint-enable */ diff --git a/src/containers/BuildingList/index.js b/src/containers/BuildingList/index.js new file mode 100644 index 0000000000000000000000000000000000000000..d8d33cfc546553ea6e96d41231e3aa6b84a12f1a --- /dev/null +++ b/src/containers/BuildingList/index.js @@ -0,0 +1,81 @@ +import React, { Component, PropTypes } from 'react'; +import { connect } from 'react-redux'; +import { bindActionCreators } from 'redux'; + +import { fetchBuildings } from './actions'; +import BuildingListTable from '../../components/BuildingListTable'; +import './styles.css'; + + +class BuildingList extends Component { + constructor(props) { + super(props); + + this.state = { term: '107 broadway' }; + + this.onInputChange = this.onInputChange.bind(this); + this.onFormSubmit = this.onFormSubmit.bind(this); + } + + componentDidMount() { + this.getBuildings(); + } + + onInputChange(event) { + this.setState({ term: event.target.value }); + } + + onFormSubmit(event) { + event.preventDefault(); + + this.getBuildings(); + } + + getBuildings() { + this.props.fetchBuildings(this.state.term); + } + + render() { + return ( +
+
+
+ + +
+
+ +
+ ); + } +} + +BuildingList.propTypes = { + buildings: PropTypes.arrayOf(PropTypes.shape({ + address: PropTypes.string, + bbl: PropTypes.number, + blocpower_id: PropTypes.number, + borough: PropTypes.string, + zipcode: PropTypes.number, + })), + fetchBuildings: PropTypes.func, +}; + +function mapDispatchToProps(dispatch) { + return bindActionCreators({ fetchBuildings }, dispatch); +} + +function mapStateToProps({ buildings }) { + return { buildings }; +} + +export default connect(mapStateToProps, mapDispatchToProps)(BuildingList); diff --git a/src/containers/HomePage/reducer.js b/src/containers/BuildingList/reducer.js similarity index 78% rename from src/containers/HomePage/reducer.js rename to src/containers/BuildingList/reducer.js index 077dcc302dc62e2dcec293cb01744c6634555d98..82c2717cf80d1494e41996842de6f2ce89fb2fee 100644 --- a/src/containers/HomePage/reducer.js +++ b/src/containers/BuildingList/reducer.js @@ -1,4 +1,4 @@ -import {FETCH_BUILDINGS} from './constants'; +import { FETCH_BUILDINGS } from './constants'; export default function (state = [], action) { switch (action.type) { diff --git a/src/containers/HomePage/styles.css b/src/containers/BuildingList/styles.css similarity index 100% rename from src/containers/HomePage/styles.css rename to src/containers/BuildingList/styles.css diff --git a/src/containers/HomePage/actions.js b/src/containers/HomePage/actions.js deleted file mode 100644 index f37e330b89072b7e0bfba22848bd594690bcefff..0000000000000000000000000000000000000000 --- a/src/containers/HomePage/actions.js +++ /dev/null @@ -1,30 +0,0 @@ -import 'whatwg-fetch'; -import {FETCH_BUILDINGS} from './constants' - -const ROOT_URL = 'http://localhost:5404/building/'; - -export function fetchBuildings(address) { - const myHeaders = new Headers({"x-blocpower-app-key": "04a6b12c-d3d3-4ad2-afe4-b22fd0215578"}); - - const myInit = { - method: 'GET', - headers: myHeaders, - mode: 'cors', - cache: 'default' - }; - - const url = `${ROOT_URL}?address=${address}`; - - const request = fetch(url, myInit).then(function (response) { - return response.json() - - }).catch(function (error) { - console.log('request failed for buildings', error) - - }); - - return { - type: FETCH_BUILDINGS, - payload: request - }; -} diff --git a/src/containers/HomePage/constants.js b/src/containers/HomePage/constants.js deleted file mode 100644 index bdea640008155219b368de0dfeeeaa17123ae1c0..0000000000000000000000000000000000000000 --- a/src/containers/HomePage/constants.js +++ /dev/null @@ -1 +0,0 @@ -export const FETCH_BUILDINGS = 'FETCH_BUILDINGS'; diff --git a/src/containers/HomePage/index.js b/src/containers/HomePage/index.js deleted file mode 100644 index 0664832b64e5eb78dddcb118cdcd216f42510f63..0000000000000000000000000000000000000000 --- a/src/containers/HomePage/index.js +++ /dev/null @@ -1,67 +0,0 @@ -import React, {Component} from 'react'; -import {connect} from 'react-redux'; -import {bindActionCreators} from 'redux'; -import {fetchBuildings} from './actions' - -import BuildingList from '../../components/BuildingList' -import './styles.css'; - - -class HomePage extends Component { - constructor(props) { - super(props); - - this.state = {term: '107 broadway'}; - - this.onInputChange = this.onInputChange.bind(this); - this.onFormSubmit = this.onFormSubmit.bind(this); - } - - componentDidMount() { - this.getBuildings(); - } - - getBuildings() { - this.props.fetchBuildings(this.state.term); - } - - onInputChange(event) { - this.setState({term: event.target.value}); - } - - onFormSubmit(event) { - event.preventDefault(); - - this.getBuildings(); - } - - render() { - return ( -
-
-
- - -
-
- -
- ) - } -} - -function mapDispatchToProps(dispatch) { - return bindActionCreators({fetchBuildings}, dispatch); -} - -function mapStateToProps({buildings}) { - return {buildings}; -} - -export default connect(mapStateToProps, mapDispatchToProps)(HomePage); diff --git a/src/index.js b/src/index.js index 252797585836c119c747af3f78092d986d08c6b2..3be4b9f95eafb2c48908ea9d3291584732ffb749 100644 --- a/src/index.js +++ b/src/index.js @@ -1,19 +1,14 @@ import React from 'react'; import ReactDOM from 'react-dom'; +import { Provider } from 'react-redux'; +import { Router } from 'react-router'; -import {Provider} from 'react-redux'; -import {createStore, applyMiddleware} from 'redux'; -import ReduxPromise from 'redux-promise'; - -import App from './containers/App'; -import rootReducer from './reducer' - -const createStoreWithMiddleware = applyMiddleware(ReduxPromise)(createStore); +import { store, history } from './store'; +import routes from './routes'; ReactDOM.render( - - + + , - document.getElementById('root') + document.getElementById('root'), ); diff --git a/src/reducer.js b/src/reducer.js index dd0cedcb55a8138bcc1e0c3d31aa506e7e7fd12a..3660b453afe4b72e2c2f41f4b58688dfd0d97113 100644 --- a/src/reducer.js +++ b/src/reducer.js @@ -1,9 +1,11 @@ -import {combineReducers} from 'redux'; +import { combineReducers } from 'redux'; +import { routerReducer } from 'react-router-redux'; -import BuildingReducer from './containers/HomePage/reducer'; +import BuildingReducer from './containers/BuildingList/reducer'; const rootReducer = combineReducers({ - buildings: BuildingReducer + routing: routerReducer, + buildings: BuildingReducer, }); export default rootReducer; diff --git a/src/routes.js b/src/routes.js new file mode 100644 index 0000000000000000000000000000000000000000..33b9879b2a1368c87a50c28d9814243b64bbd18e --- /dev/null +++ b/src/routes.js @@ -0,0 +1,9 @@ +import React from 'react'; +import { Route, IndexRoute } from 'react-router'; +import BuildingList from './containers/BuildingList'; + +module.exports = ( + + + +); diff --git a/src/store.js b/src/store.js new file mode 100644 index 0000000000000000000000000000000000000000..d397ae7334ebba491d3df136b4c1f77eba69606b --- /dev/null +++ b/src/store.js @@ -0,0 +1,26 @@ +import { createStore, applyMiddleware, compose } from 'redux'; +import ReduxPromise from 'redux-promise'; +import { browserHistory } from 'react-router'; +import { syncHistoryWithStore } from 'react-router-redux'; +import rootReducer from './reducer'; + +/* eslint-disable no-underscore-dangle */ +const composeEnhancers = + process.env.NODE_ENV !== 'production' && + typeof window === 'object' && + window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? + window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({ + // Specify here name, actionsBlacklist, actionsCreators and other options + }) : compose; + +const enhancer = composeEnhancers( + applyMiddleware(ReduxPromise), + // other store enhancers if any +); +/* eslint-enable */ + +const store = createStore(rootReducer, enhancer); + +const history = syncHistoryWithStore(browserHistory, store); + +export { store, history };