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..ea6610776f04d38582de56520c134ac07b0cb2ec 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", @@ -83,8 +84,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..b0820b49ec60b7f05aaa708494cb02a07e057dde 100644 --- a/src/index.js +++ b/src/index.js @@ -1,19 +1,13 @@ import React from 'react'; import ReactDOM from 'react-dom'; +import { Provider } from 'react-redux'; -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 from './store'; +import BuildingList from './containers/BuildingList'; ReactDOM.render( - - + + , - document.getElementById('root') + document.getElementById('root'), ); diff --git a/src/reducer.js b/src/reducer.js index dd0cedcb55a8138bcc1e0c3d31aa506e7e7fd12a..c32d6aa942e4897b02d6f5b8e822f50c71d0c271 100644 --- a/src/reducer.js +++ b/src/reducer.js @@ -1,9 +1,9 @@ -import {combineReducers} from 'redux'; +import { combineReducers } from 'redux'; -import BuildingReducer from './containers/HomePage/reducer'; +import BuildingReducer from './containers/BuildingList/reducer'; const rootReducer = combineReducers({ - buildings: BuildingReducer + buildings: BuildingReducer, }); export default rootReducer; diff --git a/src/store.js b/src/store.js new file mode 100644 index 0000000000000000000000000000000000000000..f802f9d4c277f7d705ec4e96fca6a25722a1781a --- /dev/null +++ b/src/store.js @@ -0,0 +1,13 @@ +import { createStore, applyMiddleware } from 'redux'; +import ReduxPromise from 'redux-promise'; +import rootReducer from './reducer'; + +const createStoreWithMiddleware = applyMiddleware(ReduxPromise)(createStore); + +/* eslint-disable no-underscore-dangle */ +const store = createStoreWithMiddleware(rootReducer, + window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() +); +/* eslint-enable */ + +export default store;