diff --git a/package.json b/package.json index bc8226e15..1e6326f8f 100644 --- a/package.json +++ b/package.json @@ -41,10 +41,10 @@ "deploy": "gh-pages -d dist" }, "devDependencies": { - "@babel/core": "^7.7.2", + "@babel/core": "^7.8.4", "@babel/plugin-proposal-class-properties": "^7.7.0", - "@babel/preset-env": "^7.7.1", - "@babel/preset-react": "^7.7.0", + "@babel/preset-env": "^7.8.4", + "@babel/preset-react": "^7.8.3", "babel-eslint": "^10.0.3", "babel-loader": "^8.0.6", "css-loader": "^3.2.0", diff --git a/src/components/common/Button.jsx b/src/components/common/Button.jsx index 995c6a4ee..70adfe440 100644 --- a/src/components/common/Button.jsx +++ b/src/components/common/Button.jsx @@ -24,6 +24,8 @@ const Button = ({ isStatic, disabled, style, + icon, + iconStyle, }) => { // Dynamically generates button className from props to comply with Bulma styling modifiers. const buttonClassName = classNames('button', { @@ -53,7 +55,14 @@ const Button = ({ className={buttonClassName} style={style} > - {label} + {icon && ( + + + + )} + + {label} + ); }; @@ -78,6 +87,8 @@ Button.propTypes = { isStatic: PropTypes.bool, disabled: PropTypes.bool, style: PropTypes.shape({}), + iconStyle: PropTypes.shape({}), + icon: PropTypes.string, }; Button.defaultProps = { @@ -97,4 +108,6 @@ Button.defaultProps = { isStatic: false, disabled: false, style: undefined, + iconStyle: undefined, + icon: undefined, }; diff --git a/src/components/common/Checkbox.jsx b/src/components/common/Checkbox.jsx index 93df7b6e7..92785a8f0 100644 --- a/src/components/common/Checkbox.jsx +++ b/src/components/common/Checkbox.jsx @@ -73,6 +73,7 @@ Checkbox.propTypes = { hasNoBorder: PropTypes.bool, hasBackgroundColor: PropTypes.bool, disabled: PropTypes.bool, + checked: PropTypes.bool, }; Checkbox.defaultProps = { @@ -90,4 +91,5 @@ Checkbox.defaultProps = { hasNoBorder: false, hasBackgroundColor: false, disabled: false, + checked: false, }; diff --git a/src/components/main/menu/Menu.jsx b/src/components/main/menu/Menu.jsx index 923c5b0fc..196a5c8c0 100644 --- a/src/components/main/menu/Menu.jsx +++ b/src/components/main/menu/Menu.jsx @@ -1,6 +1,11 @@ +/* eslint-disable jsx-a11y/anchor-is-valid */ +/* eslint-disable jsx-a11y/no-static-element-interactions */ +/* eslint-disable jsx-a11y/click-events-have-key-events */ import React, { useState } from 'react'; import { slide as Sidebar } from 'react-burger-menu'; + import Button from '../../common/Button'; +import NCSelector from './NCSelector'; // const buildDataUrl = () => { // return `https://data.lacity.org/resource/${dataResources[year]}.json?$select=location,zipcode,address,requesttype,status,ncname,streetname,housenumber&$where=date_extract_m(CreatedDate)+between+${startMonth}+and+${endMonth}+and+requesttype='${request}'`; @@ -24,11 +29,10 @@ const Menu = () => { return (
{ styles={{ bmMenu: { background: 'white', - boxShadow: '0 4px 5px grey', + boxShadow: '0px 4px 5px rgba(108, 108, 108, 0.3)', }, }} > - + + {/* Open/Close Button */} +
diff --git a/src/components/main/menu/NCSelector.jsx b/src/components/main/menu/NCSelector.jsx new file mode 100644 index 000000000..37639235d --- /dev/null +++ b/src/components/main/menu/NCSelector.jsx @@ -0,0 +1,170 @@ +import React, { useState } from 'react'; +import { connect } from 'react-redux'; +import propTypes from 'proptypes'; + +import { updateNC } from '../../../redux/reducers/data'; + +import { COUNCILS } from '../../common/CONSTANTS'; +import Checkbox from '../../common/Checkbox'; + +const NCSelector = ({ + updateNCList, +}) => { + const [searchValue, setSearchValue] = useState(''); + const [filteredCouncilList, setFilteredCouncilList] = useState(COUNCILS); + const [selectedCouncilList, setSelectedCouncilList] = useState( + COUNCILS.reduce((acc, council) => { + acc[council] = false; + return acc; + }, { all: false }), + ); + + const selectRowStyle = { + margin: '0 0 7px 0', + }; + + const selectRowTextStyle = { + width: '280px', + }; + + const handleSearch = (e) => { + const term = e.target.value; + const searchFilter = new RegExp(term, 'i'); + const searchList = COUNCILS.filter((council) => searchFilter.test(council)); + setFilteredCouncilList(searchList); + setSearchValue(e.target.value); + }; + + const handleSelectCouncil = (council) => { + const newSelectedCouncilList = { ...selectedCouncilList }; + + switch (council) { + case 'all': { + let value = true; + + if (newSelectedCouncilList.all) { + newSelectedCouncilList.all = false; + value = false; + } + + Object.keys(newSelectedCouncilList).forEach((c) => { + newSelectedCouncilList[c] = value; + }); + break; + } + default: + newSelectedCouncilList.all = false; + newSelectedCouncilList[council] = !newSelectedCouncilList[council]; + break; + } + + const newNCList = Object.keys(newSelectedCouncilList).filter((c) => newSelectedCouncilList[c] && c !== 'all'); + + setSelectedCouncilList(newSelectedCouncilList); + updateNCList(newNCList); + }; + + return ( +
+
+

+ + Neighborhood Council (NC) Selection + +

+
+ +
+
+
+
+ + + + +
+
+
+ +
+
+
+
+

+ SELECT ALL +

+
+
+
+
+ handleSelectCouncil('all')} + checked={selectedCouncilList?.all ?? false} + /> +
+
+
+ + {filteredCouncilList.map((council) => ( +
+
+
+

+ {council} +

+
+
+
+
+ handleSelectCouncil(council)} + checked={selectedCouncilList?.[council] ?? false} + /> +
+
+
+ ))} +
+
+
+ ); +}; + +const mapDispatchToProps = (dispatch) => ({ + updateNCList: (council) => dispatch(updateNC(council)), +}); + +NCSelector.propTypes = { + updateNCList: propTypes.func, +}; + +NCSelector.defaultProps = { + updateNCList: () => null, +}; + +export default connect(null, mapDispatchToProps)(NCSelector); diff --git a/src/redux/reducers/data.js b/src/redux/reducers/data.js index 6d9ec179f..693d2975e 100644 --- a/src/redux/reducers/data.js +++ b/src/redux/reducers/data.js @@ -22,7 +22,7 @@ export const updateRequestType = (requestType) => ({ payload: requestType, }); -export const updateNeighborhoodCouncil = (council) => ({ +export const updateNC = (council) => ({ type: types.UPDATE_NEIGHBORHOOD_COUNCIL, payload: council, }); @@ -31,7 +31,7 @@ const initialState = { startDate: null, endDate: null, requestType: 'Bulky Items', - council: null, + councils: [], }; export default (state = initialState, action) => { @@ -56,7 +56,7 @@ export default (state = initialState, action) => { case types.UPDATE_NEIGHBORHOOD_COUNCIL: return { ...state, - council: action.payload, + councils: action.payload, }; default: return state;