diff --git a/package-lock.json b/package-lock.json index 99e41198..94afac1a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5771,8 +5771,7 @@ "immutable": { "version": "3.8.2", "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.8.2.tgz", - "integrity": "sha1-wkOZUUVbs5kT2vKBN28VMOEErfM=", - "optional": true + "integrity": "sha1-wkOZUUVbs5kT2vKBN28VMOEErfM=" }, "immutable-is": { "version": "3.7.6", @@ -10324,6 +10323,11 @@ "react-side-effect": "^1.1.0" } }, + "react-immutable-proptypes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/react-immutable-proptypes/-/react-immutable-proptypes-2.1.0.tgz", + "integrity": "sha1-Aj1vObsVyXwHHp5g0A0TbqxfoLQ=" + }, "react-is": { "version": "16.3.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.3.2.tgz", @@ -10343,6 +10347,11 @@ "uuid": "^3.2.1" } }, + "react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + }, "react-reconciler": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.7.0.tgz", @@ -10355,6 +10364,35 @@ "prop-types": "^15.6.0" } }, + "react-redux": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-5.1.1.tgz", + "integrity": "sha512-LE7Ned+cv5qe7tMV5BPYkGQ5Lpg8gzgItK07c67yHvJ8t0iaD9kPFPAli/mYkiyJYrs2pJgExR2ZgsGqlrOApg==", + "requires": { + "@babel/runtime": "^7.1.2", + "hoist-non-react-statics": "^3.1.0", + "invariant": "^2.2.4", + "loose-envify": "^1.1.0", + "prop-types": "^15.6.1", + "react-is": "^16.6.0", + "react-lifecycles-compat": "^3.0.0" + }, + "dependencies": { + "hoist-non-react-statics": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.1.0.tgz", + "integrity": "sha512-MYcYuROh7SBM69xHGqXEwQqDux34s9tz+sCnxJmN18kgWh6JFdTw/5YdZtqsOdZJXddE/wUpCzfEdDrJj8p0Iw==", + "requires": { + "react-is": "^16.3.2" + } + }, + "react-is": { + "version": "16.6.3", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.6.3.tgz", + "integrity": "sha512-u7FDWtthB4rWibG/+mFbVd5FvdI20yde86qKGx4lVUTWmPlSWQ4QxbBIrrs+HnXGbxOUlUzTAP/VDmvCwaP2yA==" + } + } + }, "react-router": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/react-router/-/react-router-4.2.0.tgz", @@ -10977,6 +11015,30 @@ } } }, + "redux": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.0.1.tgz", + "integrity": "sha512-R7bAtSkk7nY6O/OYMVR9RiBI+XghjF9rlbl5806HJbQph0LJVHZrU5oaO4q70eUKiqMRqm4y07KLTlMZ2BlVmg==", + "requires": { + "loose-envify": "^1.4.0", + "symbol-observable": "^1.2.0" + }, + "dependencies": { + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + } + } + }, + "redux-immutable": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/redux-immutable/-/redux-immutable-4.0.0.tgz", + "integrity": "sha1-Ohoy32Y2ZGK2NpHw4dw15HK7yfM=" + }, "regenerate": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", @@ -12176,8 +12238,7 @@ "symbol-observable": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", - "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==", - "dev": true + "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==" }, "symbol-tree": { "version": "3.2.2", diff --git a/package.json b/package.json index 4d09ca42..c669ee36 100644 --- a/package.json +++ b/package.json @@ -6,16 +6,21 @@ "ajv": "6.5.4", "chart.js": "2.7.2", "highcharts": "^6.2.0", + "immutable": "^3.8.2", "lodash": "4.17.10", "moment": "2.22.1", "react": "16.3.2", "react-chartjs-2": "2.7.2", "react-dom": "16.3.2", "react-helmet": "5.2.0", + "react-immutable-proptypes": "^2.1.0", "react-jsx-highcharts": "^3.3.1-alpha.1", + "react-redux": "^5.0.7", "react-router": "4.2.0", "react-router-dom": "4.2.2", "react-scripts": "1.1.5", + "redux": "^4.0.1", + "redux-immutable": "^4.0.0", "semantic-ui-css": "2.2.12", "semantic-ui-react": "0.75.1" }, diff --git a/src/actions/client.js b/src/actions/client.js new file mode 100644 index 00000000..a20593d0 --- /dev/null +++ b/src/actions/client.js @@ -0,0 +1,138 @@ +export const SET_CLIENT_VALUE = 'SET_CLIENT_VALUE'; + +export const setClientValue = ({ time, route, value }) => { + return { + type: SET_CLIENT_VALUE, + payload: { + time, + route, + value, + }, + }; +}; + +export const REMOVE_MEMBER = 'REMOVE_MEMBER'; + +export const removeMember = ({ time = 'current', index }) => { + return { + type: REMOVE_MEMBER, + payload: { + time, + index, + }, + }; +}; + +export const ADD_MEMBER = 'ADD_MEMBER'; + +export const addMember = ({ time = 'current', member }) => { + return { + type: ADD_MEMBER, + payload: { + time, + member, + }, + }; +}; + +export const SET_MEMBER_IS_DISABLED = 'SET_MEMBER_IS_DISABLED'; + +export const setMemberIsDisabled = ({ time = 'current', index, isDisabled }) => { + return { + type: SET_MEMBER_IS_DISABLED, + payload: { + time, + index, + isDisabled: !!isDisabled, + }, + }; +}; + +export const SET_MEMBER_ROLE = 'SET_MEMBER_ROLE'; + +export const setMemberRole = ({ time = 'current', index, role }) => { + return { + type: SET_MEMBER_ROLE, + payload: { + time, + index, + role, + }, + }; +}; + +export const SET_MEMBER_AGE = 'SET_MEMBER_AGE'; + +export const setMemberAge = ({ time = 'current', index, age }) => { + return { + type: SET_MEMBER_AGE, + payload: { + time, + index, + age, + }, + }; +}; + +export const SET_CASH_VALUE = 'SET_CASH_VALUE'; + +export const setCashValue = ({ time, name, value }) => { + return { + type: SET_CASH_VALUE, + payload: { + time, + name, + value, + }, + }; +}; + +export const SET_HOUSING_TYPE = 'SET_HOUSING_TYPE'; + +export const setHousingType = ({ time, housingType }) => { + return { + type: SET_HOUSING_TYPE, + payload: { + time, + housingType, + }, + }; +}; + +export const SET_PAYS_UTILITY = 'SET_PAYS_UTILITY'; + +export const setPaysUtility = ({ time, utility, paysUtility }) => { + return { + type: SET_PAYS_UTILITY, + payload: { + time, + utility, + paysUtility, + }, + }; +}; + +export const SET_GETS_FUEL_ASSISTANCE = 'SET_GETS_FUEL_ASSISTANCE'; + +export const setGetsFuelAssistance = ({ time, getsAssistance }) => { + return { + type: SET_GETS_FUEL_ASSISTANCE, + payload: { + time, + getsAssistance, + }, + }; +}; + +export const SET_HAS_BENEFIT = 'SET_HAS_BENEFIT'; + +export const setHasBenefit = ({ time, benefit, value }) => { + return { + type: SET_HAS_BENEFIT, + payload: { + time, + benefit, + value: !!value, + }, + }; +}; diff --git a/src/actions/geography.js b/src/actions/geography.js new file mode 100644 index 00000000..d06bd1a6 --- /dev/null +++ b/src/actions/geography.js @@ -0,0 +1,8 @@ +export const SET_US_STATE = 'SET_US_STATE'; + +export const setUSState = ({ state }) => { + return { + type: SET_US_STATE, + payload: { state }, + }; +}; diff --git a/src/actions/index.js b/src/actions/index.js new file mode 100644 index 00000000..a0273385 --- /dev/null +++ b/src/actions/index.js @@ -0,0 +1,3 @@ +export * from './geography'; +export * from './localization'; +export * from './client'; diff --git a/src/actions/localization.js b/src/actions/localization.js new file mode 100644 index 00000000..49cf2ad6 --- /dev/null +++ b/src/actions/localization.js @@ -0,0 +1,8 @@ +export const SET_LANGUAGE = 'SET_LANGUAGE'; + +export const setLanguage = ({ language }) => { + return { + type: SET_LANGUAGE, + payload: { language }, + }; +}; diff --git a/src/App.js b/src/components/App.js similarity index 93% rename from src/App.js rename to src/components/App.js index e167d69f..2f2f5838 100644 --- a/src/App.js +++ b/src/components/App.js @@ -6,25 +6,27 @@ import { } from 'react-router-dom'; import { Helmet } from 'react-helmet'; -import { Confirmer } from './utils/getUserConfirmation'; +import { Confirmer } from '../utils/getUserConfirmation'; // CUSTOM COMPONENTS -import HomePage from './containers/HomePage'; -import AboutPage from './containers/AboutPage'; -import VisitPage from './containers/VisitPage'; -import Footer from './components/Footer'; -import Header from './components/Header'; +import HomePage from '../containers/HomePage'; +import AboutPage from '../containers/AboutPage'; +import VisitPage from '../containers/VisitPage'; +import Footer from './Footer'; +import Header from './Header'; // Development HUD -import { DevSwitch } from './containers/DevSwitch'; -import { DevHud } from './components/dev/DevHud'; +import { DevSwitch } from '../containers/DevSwitch'; +import { DevHud } from './dev/DevHud'; // Object Manipulation import { cloneDeep } from 'lodash'; -import { CLIENT_DEFAULTS } from './utils/CLIENT_DEFAULTS'; +import { CLIENT_DEFAULTS } from '../utils/CLIENT_DEFAULTS'; // LOCALIZATION -import { getTextForLanguage } from './utils/getTextForLanguage'; +import { getTextForLanguage } from '../utils/getTextForLanguage'; + + /** * Main top-level component of the app. Contains the router that controls access @@ -88,6 +90,9 @@ class App extends Component { }, distrustConfirmed: false, }; + + this.props.setLanguage({ language: this.state.langCode }); + this.props.setUSState({ state: 'MA' }); }; // End constructor() /** @@ -101,6 +106,7 @@ class App extends Component { setLanguage = (evnt, inputProps) => { let snippets = getTextForLanguage(inputProps.value); this.setState({ language: inputProps.value, snippets: snippets }); + this.props.setLanguage({ language: inputProps.value }); }; /** Set the value of a specified key in the app state's devProps. @@ -277,5 +283,4 @@ class App extends Component { }; // End render() } - export default App; diff --git a/src/configure-store.js b/src/configure-store.js new file mode 100644 index 00000000..59289b56 --- /dev/null +++ b/src/configure-store.js @@ -0,0 +1,31 @@ +/* global module, process */ + +import { createStore, compose } from 'redux'; +import { Map } from 'immutable'; +import createReducer from './reducers'; + + +export default function configureStore(initialState = Map()) { + // If Redux DevTools Extension is installed use it, otherwise use Redux compose + const composeEnhancers = process.env.NODE_ENV !== 'production' && + typeof window === 'object' && + window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ + ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) + : compose; + + const store = createStore( + createReducer(), + initialState, + composeEnhancers() + ); + + // Make reducers hot reloadable, see http://mxs.is/googmo + /* istanbul ignore next */ + if (module.hot) { + module.hot.accept('./reducers', () => { + store.replaceReducer(createReducer()); + }); + } + + return store; +} diff --git a/src/containers/App.js b/src/containers/App.js new file mode 100644 index 00000000..bda82712 --- /dev/null +++ b/src/containers/App.js @@ -0,0 +1,18 @@ +import { connect } from 'react-redux'; + +import App from '../components/App'; +import { setUSState, setLanguage } from '../actions'; + +const mapDispatchToProps = (dispatch) => { + return { + setUSState({ state }) { + dispatch(setUSState({ state })); + }, + + setLanguage({ language }) { + dispatch(setLanguage({ language })); + }, + }; +}; + +export default connect(null, mapDispatchToProps)(App); diff --git a/src/containers/VisitPage.js b/src/containers/VisitPage.js index 39f40115..442c434b 100644 --- a/src/containers/VisitPage.js +++ b/src/containers/VisitPage.js @@ -4,6 +4,7 @@ import { Responsive, } from 'semantic-ui-react'; import { Redirect } from 'react-router-dom'; +import { connect } from 'react-redux'; // DATA MANAGEMENT import { setNestedProperty } from '../utils/setNestedProperty'; @@ -26,6 +27,7 @@ import StepBar from '../components/StepBar'; import { BigButton } from '../forms/inputs'; import { ButtonReset } from '../forms/ButtonReset'; import PredictionsWarning from '../components/prompts/PredictionsWarning'; +import { setClientValue } from '../actions'; import { STEP_VALS } from '../forms/STEP_VALS'; class VisitPage extends Component { @@ -121,7 +123,9 @@ class VisitPage extends Component { routeList = route.split('/'), id = routeList[ 0 ], // `routeList` gets mutated newEvent = { time: time, route: routeList, value: value }; - + + this.props.setClientValue({ time, route: routeList, value }); + setNestedProperty(newEvent, clone, this.state.userChanged[ id ]); // Only set if the input was valid...? For now, always. // Also, userChanged should be only one step deep @@ -136,7 +140,7 @@ class VisitPage extends Component { oldHousing = clone.current.housing; } - if (clone.current.benefits.includes('section8')) { + if (clone.current.get('benefits').includes('section8')) { clone.current.housing = 'voucher'; } else { // Restore housing to previous value @@ -371,4 +375,14 @@ class VisitPage extends Component { } } -export default VisitPage; +const mapDispatchToProps = (dispatch) => { + return { + setClientValue({ time, route, value }) { + dispatch( + setClientValue({ time, route, value }) + ); + }, + }; +}; + +export default connect(null, mapDispatchToProps)(VisitPage); diff --git a/src/containers/forms/CurrentBenefits.js b/src/containers/forms/CurrentBenefits.js new file mode 100644 index 00000000..601dc862 --- /dev/null +++ b/src/containers/forms/CurrentBenefits.js @@ -0,0 +1,36 @@ +import { connect } from 'react-redux'; + +import { CurrentBenefitsStep } from '../../forms/CurrentBenefits'; +import { setHasBenefit } from '../../actions'; + +const mapStateToProps = (state) => { + return { + currentClient: state.getIn([ + 'client', + 'current', + ]), + + USState: state.getIn([ + 'geography', + 'state', + ]), + }; +}; + +const mapDispatchToProps = (dispatch) => { + return { + setHasBenefit({ benefit, value }) { + dispatch( + setHasBenefit({ + time: 'current', + benefit, + value, + }) + ); + }, + }; +}; + +const CurrentBenefitsContainer = connect(mapStateToProps, mapDispatchToProps)(CurrentBenefitsStep); + +export { CurrentBenefitsContainer as CurrentBenefitsStep }; diff --git a/src/containers/forms/CurrentExpenses.js b/src/containers/forms/CurrentExpenses.js new file mode 100644 index 00000000..cfa28f98 --- /dev/null +++ b/src/containers/forms/CurrentExpenses.js @@ -0,0 +1,60 @@ +import { connect } from 'react-redux'; + +import { CurrentExpensesStep } from '../../forms/CurrentExpenses'; + +import { setCashValue, setHousingType, setPaysUtility, setGetsFuelAssistance } from '../../actions'; + +const mapStateToProps = (state) => { + return { + currentClient: state.getIn([ + 'client', + 'current', + ]), + }; +}; + +const mapDispatchToProps = (dispatch) => { + return { + setExpenseValue({ name, value }) { + dispatch( + setCashValue({ + time: 'current', + name, + value, + }) + ); + }, + + setHousingType({ housingType }) { + dispatch( + setHousingType({ + time: 'current', + housingType, + }) + ); + }, + + setPaysUtility({ utility, paysUtility }) { + dispatch( + setPaysUtility({ + time: 'current', + utility, + paysUtility, + }) + ); + }, + + setGetsFuelAssistance({ getsAssistance }) { + dispatch( + setGetsFuelAssistance({ + time: 'current', + getsAssistance, + }) + ); + }, + }; +}; + +const CurrentExpensesStepContainer = connect(mapStateToProps, mapDispatchToProps)(CurrentExpensesStep); + +export { CurrentExpensesStepContainer as CurrentExpensesStep }; diff --git a/src/containers/forms/CurrentIncome.js b/src/containers/forms/CurrentIncome.js new file mode 100644 index 00000000..b42a5f5e --- /dev/null +++ b/src/containers/forms/CurrentIncome.js @@ -0,0 +1,32 @@ +import { connect } from 'react-redux'; + +import { CurrentIncomeStep } from '../../forms/CurrentIncome'; + +import { setCashValue } from '../../actions'; + +const mapStateToProps = (state) => { + return { + currentClient: state.getIn([ + 'client', + 'current', + ]), + }; +}; + +const mapDispatchToProps = (dispatch) => { + return { + setIncomeValue({ name, value }) { + dispatch( + setCashValue({ + time: 'current', + name, + value, + }) + ); + }, + }; +}; + +const CurrentIncomeStepContainer = connect(mapStateToProps, mapDispatchToProps)(CurrentIncomeStep); + +export { CurrentIncomeStepContainer as CurrentIncomeStep }; diff --git a/src/containers/forms/Household.js b/src/containers/forms/Household.js new file mode 100644 index 00000000..d8e22526 --- /dev/null +++ b/src/containers/forms/Household.js @@ -0,0 +1,78 @@ +import { connect } from 'react-redux'; + +import { HouseholdStep } from '../../forms/Household'; +import { + addMember, + removeMember, + setMemberIsDisabled, + setMemberRole, + setMemberAge, +} from '../../actions'; + +const time = 'current'; + +const mapStateToProps = (state) => { + return { + household: state.getIn([ + 'client', + 'current', + 'household', + ]), + }; +}; + +const mapDispatchToProps = (dispatch) => { + return { + setMemberAge({ index, age }) { + dispatch( + setMemberAge({ + time, + index, + age, + }) + ); + }, + + setMemberIsDisabled({ index, isDisabled }) { + dispatch( + setMemberIsDisabled({ + time, + index, + isDisabled, + }) + ); + }, + + setMemberRole({ index, role }) { + dispatch( + setMemberRole({ + time, + index, + role, + }) + ); + }, + + removeMember({ index }) { + dispatch( + removeMember({ + time, + index, + }) + ); + }, + + addMember({ member }) { + dispatch( + addMember({ + time, + member, + }) + ); + }, + }; +}; + +const HouseholdStepContainer = connect(mapStateToProps, mapDispatchToProps)(HouseholdStep); + +export { HouseholdStepContainer as HouseholdStep }; diff --git a/src/containers/forms/Predictions.js b/src/containers/forms/Predictions.js new file mode 100644 index 00000000..8f72d5f2 --- /dev/null +++ b/src/containers/forms/Predictions.js @@ -0,0 +1,27 @@ +import { connect } from 'react-redux'; + +import { PredictionsStep } from '../../forms/Predictions'; + +import { setCashValue } from '../../actions'; + +const mapStateToProps = (state) => { + return { client: state.get('client') }; +}; + +const mapDispatchToProps = (dispatch) => { + return { + setPredictionValue({ name, value }) { + dispatch( + setCashValue({ + time: 'future', + name, + value, + }) + ); + }, + }; +}; + +const PredictionsStepContainer = connect(mapStateToProps, mapDispatchToProps)(PredictionsStep); + +export { PredictionsStepContainer as PredictionsStep }; diff --git a/src/containers/forms/output/Summary.js b/src/containers/forms/output/Summary.js new file mode 100644 index 00000000..831026ab --- /dev/null +++ b/src/containers/forms/output/Summary.js @@ -0,0 +1,9 @@ +import { connect } from 'react-redux'; + +import { Summary } from '../../../forms/output/Summary'; + +const mapStateToProps = (state) => { + return { client: state.get('client') }; +}; + +export default connect(mapStateToProps)(Summary); diff --git a/src/forms/CurrentBenefits.js b/src/forms/CurrentBenefits.js index 7aae1c79..63e3dbb0 100644 --- a/src/forms/CurrentBenefits.js +++ b/src/forms/CurrentBenefits.js @@ -1,6 +1,5 @@ // REACT COMPONENTS import React from 'react'; -import cloneDeep from 'lodash/cloneDeep'; // PROJECT COMPONENTS import { FormPartsContainer } from './FormPartsContainer'; @@ -14,8 +13,8 @@ import { allBenefitOrders } from '../programs/allBenefitOrders'; * * @function * @param {object} props - * @property {object} props.current Client current info. - * @property {function} props.updateClientValue Updates state upstream. + * @property {Immutable.Map} props.currentClient Client current info. + * @property {function} props.setHasBenefit Sets whether the client has a particular benefit. * @property {function} props.snippets Uses user chosen language-specific * snippets. * @@ -23,37 +22,11 @@ import { allBenefitOrders } from '../programs/allBenefitOrders'; */ class CurrentBenefitsContent extends React.Component { handleRadioChange = (event, inputProps) => { - const benefitName = inputProps.name; - - const route = 'benefits'; - - const value = cloneDeep(this.props.current.benefits); - - if (inputProps.value) { - value.push(benefitName); - value.sort( - // Make sure benefits are in the correct order - (a, b) => { - const aIndex = this.props.benefits.indexOf(a); - const bIndex = this.props.benefits.indexOf(b); - - return aIndex - bIndex; - } - ); - } - else { - const index = value.indexOf(benefitName); - - if (index >= 0) { - value.splice(index, 1); - } - } - - this.props.updateClientValue(event, { route, value, time: 'current' }); + this.props.setHasBenefit({ benefit: inputProps.name, value: inputProps.value }); }; render() { - const { current, snippets, benefits } = this.props; + const { currentClient, snippets, benefits } = this.props; const components = []; @@ -74,7 +47,7 @@ class CurrentBenefitsContent extends React.Component { snippets={ snippets } labelText={ labelText } onChange={ this.handleRadioChange } - checked = { current.benefits.includes(benefit) } + checked = { currentClient.get('benefits').includes(benefit) } name = { benefit } /> ); } @@ -92,15 +65,15 @@ class CurrentBenefitsContent extends React.Component { * * @function * @param {object} props - * @property {function} props.updateClientValue Updates state upstream. * @property {object} props.navData Bottom row buttons - * @property {object} props.client JSON object with future and current values. + * @property {Immutable.Map} props.currentClient Immutable Map with current values. + * @property {string} props.USState the US state that the client lives in * @property {function} props.snippets Uses user chosen language-specific * snippets. * * @returns {object} Component */ -const CurrentBenefitsStep = ({ updateClientValue, navData, client, snippets }) => { +const CurrentBenefitsStep = ({ setHasBenefit, navData, currentClient, USState, snippets }) => { return ( + setHasBenefit = { setHasBenefit } + currentClient = { currentClient } + snippets = { snippets } + benefits = { allBenefitOrders[ USState ] } /> ); diff --git a/src/forms/CurrentExpenses.js b/src/forms/CurrentExpenses.js index 5f4979b2..a1ea2086 100644 --- a/src/forms/CurrentExpenses.js +++ b/src/forms/CurrentExpenses.js @@ -62,7 +62,15 @@ import { getUnder13Expenses } from '../utils/cashflow'; * * @returns React element */ -const CurrentExpensesStep = function ({ updateClientValue, navData, client, snippets }) { +const CurrentExpensesStep = function ({ + setExpenseValue, + setHousingType, + setPaysUtility, + setGetsFuelAssistance, + navData, + currentClient, + snippets, +}) { return ( ); @@ -91,15 +102,25 @@ const CurrentExpensesStep = function ({ updateClientValue, navData, client, snip * * @returns React element */ -const ExpensesFormContent = function ({ current, time, updateClientValue, snippets }) { +const ExpensesFormContent = function ({ + client, + setExpenseValue, + setHousingType, + setPaysUtility, + setGetsFuelAssistance, + snippets, +}) { let type = 'expense', - household = current.household, + household = client.get('household'), sharedProps = { - timeState: current, - type: type, - time: time, - updateClientValue: updateClientValue, + timeState: client, + type, + time: 'current', + setExpenseValue, + setHousingType, + setPaysUtility, + setGetsFuelAssistance, }; /* @todo Make a more general age-checking function to keep @@ -115,7 +136,7 @@ const ExpensesFormContent = function ({ current, time, updateClientValue, snippe // 'Elderly' here is using the lowest common denominator - SNAP standards. const isElderlyOrDisabled = function (member) { - return isDisabled(member) || member.m_age >= 60; + return isDisabled(member) || member.get('m_age') >= 60; }; const elderlyOrDisabled = getEveryMember(household, isElderlyOrDisabled), elderlyOrDisabledHeadOrSpouse = getEveryMember(elderlyOrDisabled, isHeadOrSpouse); @@ -125,16 +146,16 @@ const ExpensesFormContent = function ({ current, time, updateClientValue, snippe { under13.length > 0 ? ( + snippets = { snippets } + type = { type } + sharedProps = { sharedProps } + client = { client } + setExpenseValue = { setExpenseValue } /> ) : ( null ) } - { current.benefits.includes('snap') ? ( + { client.get('benefits').includes('snap') ? ( 0 ? ( + snippets = { snippets } + type = { type } + sharedProps = { sharedProps } + client = { client } + setExpenseValue = { setExpenseValue } /> ) : ( null ) } @@ -168,7 +189,7 @@ const ExpensesFormContent = function ({ current, time, updateClientValue, snippe * These medical expenses count for Section 8 too if the disabled * person is the head or spouse. From Appendix B, item (D) * {@link http://www.tacinc.org/media/58886/S8MS%20Full%20Book.pdf} */} - { elderlyOrDisabledHeadOrSpouse.length > 0 || (current.benefits.includes('snap') && elderlyOrDisabled.length > 0) ? ( + { elderlyOrDisabledHeadOrSpouse.length > 0 || (client.get('benefits').includes('snap') && elderlyOrDisabled.length > 0) ? ( + setHousingType={ setHousingType } + setExpenseValue={ setExpenseValue } + setGetsFuelAssistance={ setGetsFuelAssistance } + setPaysUtility={ setPaysUtility } /> {/* Premature feature temporarily hidden to avoid messy revert -const Under13 = function ({ snippets, type, sharedProps, current, updateClientValue }) { +const Under13 = function ({ snippets, type, sharedProps, client, setExpenseValue }) { return (
@@ -228,12 +251,12 @@ const Under13 = function ({ snippets, type, sharedProps, current, updateClientVa Legally obligated child support
@@ -286,7 +310,7 @@ const DependentsOver12 = function ({ type, sharedProps }) { }; // Ends -const ElderlyOrDisabledAssistance = function ({ current, type, sharedProps, updateClientValue }) { +const ElderlyOrDisabledAssistance = function ({ current, type, sharedProps, setExpenseValue }) { return (
@@ -312,7 +336,7 @@ const ElderlyOrDisabledAssistance = function ({ current, type, sharedProps, upda propData = {{ client: current, childPropName: `earnedBecauseOfAdultCare`, - update: updateClientValue, + update: setExpenseValue, }} CashFlowRow = { Housing - { current.housing === 'voucher' ? ( + { client.get('housing') === 'voucher' ? ( null ) : (
What is your housing situation?
+ setValue = { ensureRouteAndValue } /> + setValue = { ensureRouteAndValue } /> + setValue = { ensureRouteAndValue } />
) } - - +
); }; // Ends -const HousingRadio = function ({ currentValue, label, time, updateClientValue }) { +const HousingRadio = function ({ currentValue, label, setValue }) { const value = label.toLowerCase(); @@ -432,30 +459,40 @@ const HousingRadio = function ({ currentValue, label, time, updateClientValue }) label={ label } value={ value } checked={ currentValue === value } - onChange = { updateClientValue } /> + onChange = { setValue } /> ); }; // Ends -const HousingDetails = function ({ current, type, time, updateClientValue }) { +const HousingDetails = function ({ + client, + type, + time, + setExpenseValue, + setPaysUtility, + setGetsFuelAssistance, +}) { - let housing = current.housing, + let housing = client.get('housing'), sharedProps = { - timeState: current, - current: current, - type: type, - time: time, - updateClientValue: updateClientValue, + timeState: client, + client: client, + type: type, + time: time, + setValue: setExpenseValue, }; - if (current.housing === 'voucher') { + if (client.get('housing') === 'voucher') { return (
- +
); @@ -495,16 +532,27 @@ const HousingDetails = function ({ current, type, time, updateClientValue }) { }; // Ends -const Utilities = function ({ current, type, time, updateClientValue }) { +const Utilities = function ({ + client, + setGetsFuelAssistance, + setPaysUtility, +}) { + + let hasClimate = client.get('climateControl'), + hasElectricity = client.get('nonHeatElectricity'), + hasPhone = client.get('phone'), + hasFuelAssist = client.get('fuelAssistance'); - let hasClimate = current.climateControl, - hasElectricity = current.nonHeatElectricity, - hasPhone = current.phone, - hasFuelAssist = current.fuelAssistance; + + let setChecked = function (evnt, { name, checked }) { + setPaysUtility({ + utility: name, + paysUtility: checked, + }); + }; // End setChecked() - let setChecked = function (evnt, inputProps) { - const obj = { ...inputProps, value: inputProps.checked }; - updateClientValue(evnt, obj); + const handleFuelAssistanceChanged = (event, { value }) => { + setGetsFuelAssistance({ getsAssistance: value }); }; // For keyboard access (already does spacebar) @@ -546,7 +594,7 @@ const Utilities = function ({ current, type, time, updateClientValue }) { labelText = { 'Do you get Fuel Assistance?' } checked = { hasFuelAssist } name = { 'fuelAssistance' } - onChange = { updateClientValue } /> + onChange = { handleFuelAssistanceChanged } /> diff --git a/src/forms/CurrentIncome.js b/src/forms/CurrentIncome.js index 45909412..cd6467a5 100644 --- a/src/forms/CurrentIncome.js +++ b/src/forms/CurrentIncome.js @@ -38,7 +38,7 @@ import { CashFlowInputsRow } from './cashflow'; * * @function * @param {object} props - * @property {object} props.current Client current info. Could be + * @property {Immutable.Map} props.currentClient Client current info. Could be * changed to just 'client' to allow future values in abstraction. * @property {string} props.time 'current' or 'future'. (needed?) * @property {function} props.updateClientValue Updates state upstream. @@ -46,23 +46,14 @@ import { CashFlowInputsRow } from './cashflow'; * * @returns {object} React element */ -const IncomeForm = function ({ current, time, updateClientValue, snippets }) { +const IncomeForm = function ({ client, setIncomeValue, snippets }) { let type = 'income'; - /** Makes sure values are propagated to 'future' properties if needed - * @member - * @depricated - */ - let ensureFuture = function (evnt, inputProps) { - updateClientValue(evnt, { ...inputProps, fillFuture: true }); - }; // End ensureFuture() - - let sharedProps = { - timeState: current, - time: time, - type: type, - updateClientValue: ensureFuture, + const sharedProps = { + timeState: client, + type: type, + setValue: setIncomeValue, }; return ( @@ -138,14 +129,14 @@ const IncomeForm = function ({ current, time, updateClientValue, snippets }) { /** * @function * @param {object} props - * @property {function} props.updateClientValue Updates state upstream. + * @property {function} props.setIncomeValue Updates the state of an income value. * @property {object} props.navData Bottom row buttons. * @property {object} props.client JSON object with `future` and `current` props. * @property {function} props.snippets Uses user chosen language-specific text. * * @returns {object} React element */ -const CurrentIncomeStep = function ({ updateClientValue, navData, client, snippets }) { +const CurrentIncomeStep = function ({ setIncomeValue, navData, currentClient, snippets }) { // `props` is a cloned version of the original props. References broken. return ( @@ -155,9 +146,8 @@ const CurrentIncomeStep = function ({ updateClientValue, navData, client, snippe navData = { navData } formClass = { `income` }> ); diff --git a/src/forms/Household.js b/src/forms/Household.js index ce0f5113..8b66e43a 100644 --- a/src/forms/Household.js +++ b/src/forms/Household.js @@ -20,10 +20,6 @@ import { hasOnlyNonNegWholeNumberChars, } from '../utils/validators'; -// OBJECT MANIPULATION -import { cloneDeep } from 'lodash'; - - // ====================== // GENERICS // ====================== @@ -113,239 +109,228 @@ const MemberButton = function ({ basic, color, iconName, className, onClick }) { // UNIQUE // ====================== -const Role = function ({ member, setMember, snippets }) { - - let ThisRole = null, - margin = '0'; - - if (member.index === 0) { - - ThisRole = { snippets.i_headOfHousehold }; - - } else if (member.index === 1) { - - margin = '-1em'; - - let options = [ - { text: snippets.i_spouse, value: 'spouse' }, - { text: snippets.i_childOther, value: 'member' }, - ]; - - ThisRole = ; - - } else { +class Role extends React.PureComponent { + handleRoleChange = (event, { value }) => { + this.props.onChange({ role: value }); + }; - ThisRole = { snippets.i_childOther }; + render() { + const { member, snippets } = this.props; + + let ThisRole = null, + margin = '0'; + + if (this.props.index === 0) { + + ThisRole = { snippets.i_headOfHousehold }; + + } else if (this.props.index === 1) { + + margin = '-1em'; + + const options = [ + { text: snippets.i_spouse, value: 'spouse' }, + { text: snippets.i_childOther, value: 'member' }, + ]; + + ThisRole = ; + + } else { + + ThisRole = { snippets.i_childOther }; + + } + + // Styles will have to be adjusted. + return ( +
+ { ThisRole } +
+ ); } +} // End Role(<>) - // Styles will have to be adjusted. - return ( -
- { ThisRole } -
- ); -}; // End Role(<>) - - -const MemberField = function ({ household, time, setHousehold, updateClientValue, snippets }, indx) { - - let member = household[ indx ], - routeStart = 'household/' + indx + '/'; - member.index = indx; // Just needed as member prop in this file - - - let onMemberChange = function (evnt, inputProps) { - let route = routeStart + inputProps.name; - let data = { route: route, value: inputProps.value }; - updateClientValue(evnt, data); - }; - - - let onMemberChecked = function (evnt, inputProps) { - let route = routeStart + inputProps.name; - let data = { route: route, value: inputProps.checked }; - updateClientValue(evnt, data); +class MemberField extends React.PureComponent { + handleDisabledChecked = (event, { checked }) => { + this.props.onIsDisabledChange({ isDisabled: checked, index: this.props.index }); }; - - let removeMember = function (evnt, inputProps) { - household.splice(indx, 1); - setHousehold(evnt, household); - }; // End removeMember() - - - // For keyboard access (already does spacebar) - let onKeyDown = function (evnt) { - if (evnt.key === `Enter`) { - evnt.target.click(); + handleKeyDown = (event) => { + // For keyboard access (already does spacebar) + if (event.key === 'Enter') { + event.target.click(); } }; - - // The font size thing is a bit weird, but... later - return ( - - - - { indx > 0 ? ( - - ) : ( - - { household.length > 1 ? ( - - ) : ( - null - ) } - - ) } - - - - - - - - - - - - - - - - ); - -}; // End MemberField() - - -const getMembers = function (current, time, setHousehold, updateClientValue, snippets) { - - let household = current.household, - props = { - household: household, - time: time, - setHousehold: setHousehold, - updateClientValue: updateClientValue, - snippets: snippets, - }; - - let mems = []; - for (let memi = 0; memi < household.length; memi++) { - mems.push(MemberField(props, memi)); + handleRoleChange = ({ role }) => { + this.props.onRoleChange({ role, index: this.props.index }); }; - return mems; - -}; // End getMembers() - - -const HouseholdContent = function ({ current, time, updateClientValue, snippets }) { - - // Don't mutate state properties - let household = cloneDeep(current.household); - - - let setHousehold = function (evnt, newHousehold) { - - let obj = { - route: 'household', - value: newHousehold, - }; - - updateClientValue(evnt, obj); - - }; // End setHousehold() - + handleAgeChange = (event, { value }) => { + this.props.onAgeChange({ age: value, index: this.props.index }); + }; - let addMember = function (evnt, inputProps) { + handleRemove = () => { + this.props.remove({ index: this.props.index }); + }; + render() { + const { household, member, snippets, canRemove, index } = this.props; + + // The font size thing is a bit weird, but... later + return ( + + + + { canRemove ? ( + + ) : ( + + { household.length > 1 ? ( + + ) : ( + null + ) } + + ) } + + + + + + + + + + + + + + + + ); + } +} // End MemberField() + +class HouseholdContent extends React.PureComponent { + getMembers = () => { + const { household, setMemberAge, setMemberIsDisabled, setMemberRole, snippets } = this.props; + + return household.map( + (member, index) => { + return ( + 0 } + index = { index } + onIsDisabledChange = { setMemberIsDisabled } + onAgeChange = { setMemberAge } + onRoleChange = { setMemberRole } /> + ); + } + ).toArray(); + }; // End getMembers() + + addMember = () => { let member; - if (household.length === 1) { + if (this.props.household.size === 1) { member = { m_age: 30, m_role: 'spouse', m_disabled: false }; } else { member = { m_age: 12, m_role: 'member', m_disabled: false }; } - household.push(member); - setHousehold(evnt, household); - + this.props.addMember({ member }); }; // End addMember() + + render() { + const { snippets } = this.props; + + return ( +
+
+ + { snippets.i_role } + { snippets.i_age } + { snippets.i_disabled } +
+ + { this.getMembers() } + + - - return ( -
-
- - { snippets.i_role } - { snippets.i_age } - { snippets.i_disabled }
- - { getMembers(current, time, setHousehold, updateClientValue, snippets) } - - - -
- ); - -}; // End HouseholdContent() + ); + } +} // End HouseholdContent() // `props` is a cloned version of the original props. References broken. -const HouseholdStep = function ({ updateClientValue, navData, client, snippets }) { +const HouseholdStep = function ({ + setMemberIsDisabled, + setMemberRole, + setMemberAge, + addMember, + removeMember, + navData, + household, + snippets, +}) { return ( ); diff --git a/src/forms/Predictions.js b/src/forms/Predictions.js index d77c5735..281daf77 100644 --- a/src/forms/Predictions.js +++ b/src/forms/Predictions.js @@ -6,7 +6,7 @@ import { FormPartsContainer } from './FormPartsContainer'; import { IntervalColumnHeadings } from '../components/headings'; import { CashFlowInputsRow } from './cashflow'; import { GraphHolder } from './output/GraphHolder'; -import { Summary } from './output/Summary'; +import Summary from '../containers/forms/output/Summary'; import { BenefitsTable } from './output/BenefitsTable'; import { StackedBarGraph } from './output/StackedBarGraph'; import { StackedAreaGraph } from './output/StackedAreaGraph'; @@ -23,15 +23,13 @@ import { BenefitsLines } from './output/BenefitsLines'; * @function * @param {object} props * @param {object} props.future Client future/predictive data. - * @param {string} props.time Used in class names. Meant to make - * this more easily decoupled in future. - * @param {function} props.updateClientValue Update client state + * @param {function} props.setPredictionValue Update client state * value. * @param {object} props.snippets Language-specific text * * @returns {object} React element */ -const IncomeForm = function ({ future, time, updateClientValue, snippets }) { +const IncomeForm = function ({ future, setPredictionValue, snippets }) { let type = 'income'; @@ -41,8 +39,8 @@ const IncomeForm = function ({ future, time, updateClientValue, snippets }) { { snippets.i_futureIncomeQuestion } @@ -150,7 +148,7 @@ const TabbedVisualizations = ({ client, openFeedback, snippets }) => { }; -const PredictionsStep = function ({ updateClientValue, navData, client, snippets, openFeedback }) { +const PredictionsStep = function ({ setPredictionValue, navData, client, snippets, openFeedback }) { return (
@@ -190,7 +187,7 @@ const PredictionsStep = function ({ updateClientValue, navData, client, snippets - @@ -199,4 +196,3 @@ const PredictionsStep = function ({ updateClientValue, navData, client, snippets }; // End FutureIncomeStep() Component export { PredictionsStep }; - diff --git a/src/forms/STEP_VALS.js b/src/forms/STEP_VALS.js index 1350a6f6..d2e59a0f 100644 --- a/src/forms/STEP_VALS.js +++ b/src/forms/STEP_VALS.js @@ -1,8 +1,8 @@ -import { CurrentIncomeStep } from './CurrentIncome'; -import { CurrentExpensesStep } from './CurrentExpenses'; -import { PredictionsStep } from './Predictions'; -import { HouseholdStep } from './Household'; -import { CurrentBenefitsStep } from './CurrentBenefits'; +import { CurrentIncomeStep } from '../containers/forms/CurrentIncome'; +import { CurrentExpensesStep } from '../containers/forms/CurrentExpenses'; +import { PredictionsStep } from '../containers/forms/Predictions'; +import { HouseholdStep } from '../containers/forms/Household'; +import { CurrentBenefitsStep } from '../containers/forms/CurrentBenefits'; export const STEP_VALS = [ { diff --git a/src/forms/cashflow.js b/src/forms/cashflow.js index adba0845..37a6e051 100644 --- a/src/forms/cashflow.js +++ b/src/forms/cashflow.js @@ -1,5 +1,5 @@ // REACT COMPONENTS -import React, { Component } from 'react'; +import React from 'react'; import { Form } from 'semantic-ui-react'; // PROJECT COMPONENTS @@ -11,7 +11,6 @@ import { toMonthlyAmount } from '../utils/math'; import { isNonNegNumber, hasOnlyNonNegNumberChars } from '../utils/validators'; import { toMoneyStr } from '../utils/prettifiers'; - /** Contains cash flow inputs, their label, and any user feedback * * @function @@ -72,12 +71,13 @@ const maximum_value_yearly = 999999.99; * @param {object} props.updateClientValue Updates client state * @param {object} props.children Text for the row label */ -class CashFlowInputsRow extends Component { +class CashFlowInputsRow extends React.Component { constructor(props) { super(props); this.state = { valid: true, message: null }; } + // Special store validator that handles maximums and sets error message cashFlowStoreValidator = (max) => { return (str) => { @@ -103,18 +103,19 @@ class CashFlowInputsRow extends Component { }; }; + setValue = (event, { value }, { interval }) => { + this.props.setValue({ + name: this.props.generic, + value: toMonthlyAmount[ interval ](event, value), + }); + }; + onBlur = (evnt) => { this.setState({ valid: true, message: null }); }; render() { - let { generic, timeState, updateClientValue, children } = this.props; - - let updateClient = function (evnt, inputProps, data) { - let monthly = toMonthlyAmount[ data.interval ](evnt, inputProps.value), - obj = { name: generic, value: monthly }; - updateClientValue(evnt, obj); - }; + const { children, generic, timeState } = this.props; /* Get the time ('future' or 'current') monthly value unless there is * none, in which case, get the 'current' monthly cash flow value @@ -123,18 +124,17 @@ class CashFlowInputsRow extends Component { * @todo Add some kind of UI indication when it's the same as the 'current' * value. What if some of the row's values are the same and some are * different? */ - let baseVal = timeState[ generic ], - baseProps = { - name: generic, - className: 'cashflow-column', - store: updateClient, - displayValidator: hasOnlyNonNegNumberChars, - format: toMoneyStr, - onBlur: this.onBlur, - }; - - let cashFlowStoreValidator = this.cashFlowStoreValidator; - + const baseVal = timeState.get(generic); + + const baseProps = { + name: generic, + className: 'cashflow-column', + store: this.setValue, + displayValidator: hasOnlyNonNegNumberChars, + format: toMoneyStr, + onBlur: this.onBlur, + }; + return ( @@ -161,7 +161,6 @@ class CashFlowInputsRow extends Component { } } - /** Show a value, or the sum of multiple values, of data * that the user has already put in from another input. * @@ -171,7 +170,7 @@ class CashFlowInputsRow extends Component { */ const CashFlowDisplayRow = function ({ generic, value, timeState, children }) { - let baseVal = value || timeState[ generic ], + let baseVal = value || timeState.get(generic), colClassName = `cashflow-column`, weekly = toMoneyStr(baseVal / (4 + 1 / 3)), monthly = toMoneyStr(baseVal), @@ -204,18 +203,17 @@ const CashFlowDisplayRow = function ({ generic, value, timeState, children }) { * @param {object} props * @param {object} props.inputProps Key name, validators, and onBlur * @param {object} props.baseValue Start value of field? - * @param {object} props.updateClientValue Updates client state + * @param {object} props.setValue Updates client state * @param {object} props.rowProps `label`, `validRow`, `message` * * @returns Component */ -const MonthlyCashFlowRow = function ({ inputProps, baseValue, updateClientValue, rowProps }) { - +const MonthlyCashFlowRow = function ({ inputProps, baseValue, setValue, rowProps }) { inputProps = { ...inputProps, // name, validators, and onBlur className: 'cashflow-column', format: toMoneyStr, - store: updateClientValue, + store: setValue, }; return ( @@ -229,7 +227,6 @@ const MonthlyCashFlowRow = function ({ inputProps, baseValue, updateClientValue, }; // End - // Ideas of how to handle a different styling situation // (if we swap the input and label positions) diff --git a/src/forms/inputs.js b/src/forms/inputs.js index d687c959..b6598e99 100644 --- a/src/forms/inputs.js +++ b/src/forms/inputs.js @@ -175,6 +175,11 @@ class ManagedNumberField extends Component { let valid = storeValidator(inputProps.value); if (valid) { + inputProps = { + ...inputProps, + value: Number(inputProps.value), + }; + store(evnt, inputProps, otherData); } this.setState({ focusedVal: focusedVal, valid: valid }); diff --git a/src/forms/output/BenefitsLineGraph.js b/src/forms/output/BenefitsLineGraph.js index daa0bb33..4697730b 100644 --- a/src/forms/output/BenefitsLineGraph.js +++ b/src/forms/output/BenefitsLineGraph.js @@ -54,7 +54,8 @@ class BenefitsLineGraph extends Component { } const xRange = _.range(limits.min, max, interval), // x-axis/earned income numbers - datasets = getChartData(xRange, multiplier, client, activePrograms, extraProps); + // @todo: make this use Immutable.js collections + datasets = getChartData(xRange, multiplier, client.toJS(), activePrograms, extraProps); // If there's no data to show, don't show the table if (datasets.length === 0) { @@ -63,8 +64,12 @@ class BenefitsLineGraph extends Component { // react-chartjs-2 keeps references to plugins, so we // have to mutate that reference - const earned = client.future.earned * multiplier, - hack = this.state.verticalLine; + const earned = client.getIn([ + 'future', + 'earned', + ]) * multiplier; + + const hack = this.state.verticalLine; hack.xRange = xRange; hack.earned = earned; diff --git a/src/forms/output/BenefitsLines.js b/src/forms/output/BenefitsLines.js index 5946bcfa..6791c7e9 100644 --- a/src/forms/output/BenefitsLines.js +++ b/src/forms/output/BenefitsLines.js @@ -102,7 +102,10 @@ class BenefitsLinesComp extends Component { const multiplier = multipliers[ timescale ], resources = activePrograms, - currentEarned = client.current.earned * multiplier, + currentEarned = client.getIn([ + 'current', + 'earned', + ]) * multiplier, getText = this.getTranslatedText; // Adjust to time-interval. Highcharts will round @@ -111,7 +114,8 @@ class BenefitsLinesComp extends Component { interval = ((max / 100) / 10) * 30; const xRange = range(limits.min, max, interval), // x-axis/earned income numbers - datasets = getChartData(xRange, multiplier, client, resources, {}); + // @todo make this use Immutable.js collections + datasets = getChartData(xRange, multiplier, client.toJS(), resources, {}); // Individual benefit lines const lines = []; diff --git a/src/forms/output/BenefitsTable.js b/src/forms/output/BenefitsTable.js index 9e67f2e0..17ce29b1 100644 --- a/src/forms/output/BenefitsTable.js +++ b/src/forms/output/BenefitsTable.js @@ -5,9 +5,6 @@ import { Table } from 'semantic-ui-react'; // BENEFIT LOGIC import { applyAndPushBenefits } from '../../programs/applyAndPushBenefits'; -// OBJECT MANIPULATION -import { cloneDeep } from 'lodash'; - const getSignSymbol = function (num) { if (num > 0) { @@ -21,7 +18,8 @@ const getSignSymbol = function (num) { const BenefitsTable = function ({ client, snippets }) { - const clone = cloneDeep(client); + // @todo: make applyAndPushBenefits() work with Immutable.js collections + const clone = client.toJS(); const curr = clone.current; let allData = {}, diff --git a/src/forms/output/GraphHolder.js b/src/forms/output/GraphHolder.js index 7cfbf99e..87bf755b 100644 --- a/src/forms/output/GraphHolder.js +++ b/src/forms/output/GraphHolder.js @@ -19,11 +19,12 @@ class GraphHolder extends Component { render () { const { activeID } = this.state, - { Graph, client, snippets } = this.props, - { current } = client, - // The ids later used to access all program-specific data and functions - // Only active programs are added - activePrograms = [ ...current.benefits ]; + { Graph, client, snippets } = this.props; + const current = client.get('current'); + + // The ids later used to access all program-specific data and functions + // Only active programs are added + const activePrograms = current.get('benefits').toArray(); if (activePrograms.length === 0) { return { snippets.i_noBenefitsSelected }; diff --git a/src/forms/output/StackedAreaGraph.js b/src/forms/output/StackedAreaGraph.js index 707e4f85..c667d74f 100644 --- a/src/forms/output/StackedAreaGraph.js +++ b/src/forms/output/StackedAreaGraph.js @@ -51,14 +51,18 @@ class StackedAreaGraph extends Component { withEarned.unshift('earned'); // Adjust to time-interval, round to hundreds - const earned = client.future.earned * multiplier, - max = Math.max(earned, limits.max * multiplier), + const earned = client.getIn([ + 'future', + 'earned', + ]) * multiplier; + const max = Math.max(earned, limits.max * multiplier), xMax = Math.ceil(max / 100) * 100, xMin = Math.ceil(limits.min * multiplier / 100) * 100, interval = Math.ceil(((xMax - xMin) / 100) / 10) * 10, xRange = _.range(xMin, xMax + interval, interval), extraProps = { earned: { fill: 'origin' }}, - datasets = getChartData(xRange, multiplier, client, withEarned, extraProps); + // @todo: refactor visualizations to use Immutable collections + datasets = getChartData(xRange, multiplier, client.toJS(), withEarned, extraProps); // react-chartjs-2 keeps references to plugins, so we // have to mutate that reference diff --git a/src/forms/output/StackedBarGraph.js b/src/forms/output/StackedBarGraph.js index 754751c0..bd42b156 100644 --- a/src/forms/output/StackedBarGraph.js +++ b/src/forms/output/StackedBarGraph.js @@ -13,9 +13,6 @@ import { applyAndPushBenefits } from '../../programs/applyAndPushBenefits'; // Colors and text for parts of the chart import { PROGRAM_CHART_VALUES } from '../../utils/charts/PROGRAM_CHART_VALUES'; -// OBJECT MANIPULATION -import { cloneDeep } from 'lodash'; - /** Visual representation of the table * @@ -27,8 +24,8 @@ import { cloneDeep } from 'lodash'; * and future. All client props are needed. */ const StackedBarGraph = function({ client }) { - - let clone = cloneDeep(client), + // @todo make applyAndPushBenefits() use Immutable.js collections + let clone = client.toJS(), curr = clone.current; const allData = {}, diff --git a/src/forms/output/Summary.js b/src/forms/output/Summary.js index 65ca9397..f3b7183b 100644 --- a/src/forms/output/Summary.js +++ b/src/forms/output/Summary.js @@ -1,5 +1,7 @@ // REACT COMPONENTS import React from 'react'; +import PropTypes from 'prop-types'; +import ImmutablePropTypes from 'react-immutable-proptypes'; import { Header, Button } from 'semantic-ui-react'; // DATA @@ -7,7 +9,6 @@ import { Header, Button } from 'semantic-ui-react'; import { PROGRAM_CHART_VALUES } from '../../utils/charts/PROGRAM_CHART_VALUES'; // DATA MANIPULATION -import { cloneDeep } from 'lodash'; import { toMoneyStr } from '../../utils/prettifiers'; // BENEFIT LOGIC @@ -56,7 +57,7 @@ let totalLastItemsOfArraysInObject = function (accumulated) { * array of numerical values (which are meant to be money * values right now). * @param {array} sourceObject.earned Earned income values. - * @param {int} Which item in each array should be used to + * @param {int} index Which item in each array should be used to * accumulate values. * * @example @@ -192,10 +193,10 @@ let fillInMoneyValues = (keys, sourceObject, index) => { * @returns {object} */ let getBenefitData = function(client, resourceKeys) { - - let clone = cloneDeep(client), - // This is the data we need in the groupings we need it - result = { + // @todo: refactor to use Immutable collections + client = client.toJS(); + // This is the data we need in the groupings we need it + let result = { current: null, // current money values, future: null, // future money values, diff: 0, @@ -207,7 +208,7 @@ let getBenefitData = function(client, resourceKeys) { let defaultProps = { activeBenefits: resourceKeys, dataToAddTo: accumulated, - clientToChange: clone, + clientToChange: client, timeframe: `current`, }; let currentCalcData = defaultProps; @@ -236,7 +237,7 @@ let getBenefitData = function(client, resourceKeys) { // till the client is making more money than they are now while (recoveryAmount - resultCurr.total <= 0) { - clone.future.earned += EARNED_MONTHLY_INCREMENT_AMOUNT; + client.future.earned += EARNED_MONTHLY_INCREMENT_AMOUNT; applyAndPushBenefits(futureCalcData); // If has dramatic cliff, must have recovery recoveryAmount = totalLastItemsOfArraysInObject(accumulated); @@ -273,14 +274,23 @@ const Summary = function ({ client, openFeedback, snippets }) { const resourceKeys = [ `earned`, - ...client.current.benefits, + ...client.getIn([ + 'current', + 'benefits', + ]).toArray(), ]; // Really quick returns if other calcs not needed if (resourceKeys.length <= 1) { return snippets.i_noBenefitsChosen; } - if (client.future.earned === client.current.earned) { + if (client.getIn([ + 'future', + 'earned', + ]) === client.getIn([ + 'current', + 'earned', + ])) { return snippets.i_noFutureChange; } @@ -326,8 +336,9 @@ const Summary = function ({ client, openFeedback, snippets }) { let numBenefits = current.benefits.length; for (let benefiti = 0; benefiti < numBenefits; benefiti++) { - let cBenefit = current.benefits[ benefiti ], - fBenefit = future.benefits[ benefiti ]; + let cBenefit = current.benefits[ benefiti ]; + + let fBenefit = future.benefits[ benefiti ]; benefitList.push(
  • @@ -429,6 +440,11 @@ const Summary = function ({ client, openFeedback, snippets }) { }; // Ends +Summary.propTypes = { + client: ImmutablePropTypes.map, + openFeedback: PropTypes.func, +}; + export { Summary, totalLastItemsOfArraysInObject, diff --git a/src/forms/rentFields.js b/src/forms/rentFields.js index 7538b29f..3570f44f 100644 --- a/src/forms/rentFields.js +++ b/src/forms/rentFields.js @@ -7,6 +7,13 @@ import { isNonNegNumber, hasOnlyNonNegNumberChars } from '../utils/validators'; class RentShareField extends Component { state = { valid: true, message: null }; + setValue = (event, inputProps) => { + this.props.setValue({ + name: inputProps.name, + value: inputProps.value, + }); + }; + storeValidator = (ownValue) => { let message = null, valid = true; @@ -14,7 +21,7 @@ class RentShareField extends Component { if (!isPosNum) { valid = false; } else { - valid = Number(ownValue) <= this.props.timeState[ 'contractRent' ]; + valid = Number(ownValue) <= this.props.timeState.get('contractRent'); if (!valid) { message = 'Rent share must be less than contract rent'; } @@ -29,7 +36,7 @@ class RentShareField extends Component { }; render() { - const { timeState, updateClientValue } = this.props, + const { timeState } = this.props, { valid, message } = this.state; const inputProps = { @@ -47,11 +54,11 @@ class RentShareField extends Component { return ( + inputProps = { inputProps } + baseValue = { timeState.get('rentShare') } + includes = { [ 'monthly' ] } + setValue = { this.setValue } + rowProps = { rowProps } /> ); } }; // End @@ -60,6 +67,13 @@ class RentShareField extends Component { class ContractRentField extends Component { state = { valid: true, message: null }; + setValue = (event, inputProps) => { + this.props.setValue({ + name: inputProps.name, + value: inputProps.value, + }); + }; + storeValidator = (ownValue) => { let message = null, valid = true; @@ -67,7 +81,7 @@ class ContractRentField extends Component { if (!isPosNum) { valid = false; } else { - valid = ownValue >= this.props.timeState[ 'rentShare' ]; + valid = ownValue >= this.props.timeState.get('rentShare'); if (!valid) { message = 'Contract rent must be more than rent share'; } @@ -82,7 +96,7 @@ class ContractRentField extends Component { }; render() { - const { timeState, updateClientValue } = this.props, + const { timeState } = this.props, { valid, message } = this.state; const inputProps = { @@ -101,16 +115,16 @@ class ContractRentField extends Component { return ( ); } }; // End -const PlainRentRow = function ({ timeState, updateClientValue }) { +const PlainRentRow = function ({ timeState, setValue }) { const inputProps = { name: 'rent', @@ -127,9 +141,9 @@ const PlainRentRow = function ({ timeState, updateClientValue }) { return ( ); diff --git a/src/index.js b/src/index.js index 295c8e36..7d625d24 100644 --- a/src/index.js +++ b/src/index.js @@ -1,11 +1,23 @@ import React from 'react'; import ReactDOM from 'react-dom'; +import { Provider } from 'react-redux'; import './index.css'; import './styles/dev.css'; -import App from './App'; +import App from './containers/App'; +import getStore from './store'; import registerServiceWorker from './utils/registerServiceWorker'; import 'semantic-ui-css/semantic.min.css'; -ReactDOM.render(, document.getElementById('root')); +const store = getStore(); + +ReactDOM.render( + ( + + + + ) + , + document.getElementById('root') +); registerServiceWorker(); diff --git a/src/reducers/client.js b/src/reducers/client.js new file mode 100644 index 00000000..8186ee65 --- /dev/null +++ b/src/reducers/client.js @@ -0,0 +1,205 @@ +import { fromJS, Set } from 'immutable'; + +import { CLIENT_DEFAULTS } from '../utils/CLIENT_DEFAULTS'; +import { + SET_CLIENT_VALUE, + ADD_MEMBER, + REMOVE_MEMBER, + SET_MEMBER_IS_DISABLED, + SET_MEMBER_ROLE, + SET_MEMBER_AGE, + SET_CASH_VALUE, + SET_HOUSING_TYPE, + SET_PAYS_UTILITY, + SET_GETS_FUEL_ASSISTANCE, + SET_HAS_BENEFIT, +} from '../actions'; + +const getDefaultClients = () => { + return fromJS(CLIENT_DEFAULTS) + .updateIn( + [ + 'current', + 'benefits', + ], + (benefits) => { + return Set(benefits); + } + ).updateIn( + [ + 'future', + 'benefits', + ], + (benefits) => { + return Set(benefits); + } + ); +}; + +const clientReducer = ( + state = getDefaultClients(), + action +) => { + switch (action.type) { + case SET_CLIENT_VALUE: { + const { time, route, value } = action.payload; + return state.setIn([ + time, + ...route, + ], fromJS(value)); + } + + case REMOVE_MEMBER: { + const { index, time } = action.payload; + + return state.deleteIn( + [ + time, + 'household', + index, + ] + ); + } + + case ADD_MEMBER: { + const { member, time } = action.payload; + + return state.updateIn( + [ + time, + 'household', + ], + (members) => { + return members.push(fromJS(member)); + } + ); + } + + case SET_MEMBER_IS_DISABLED: { + const { time, index, isDisabled } = action.payload; + + return state.setIn( + [ + time, + 'household', + index, + 'm_disabled', + ], + isDisabled + ); + } + + case SET_MEMBER_ROLE: { + const { time, index, role } = action.payload; + + return state.setIn( + [ + time, + 'household', + index, + 'm_role', + ], + role + ); + } + + case SET_MEMBER_AGE: { + const { time, index, age } = action.payload; + + return state.setIn( + [ + time, + 'household', + index, + 'm_age', + ], + age + ); + } + + case SET_CASH_VALUE: { + const { time, name, value } = action.payload; + + return state.setIn( + [ + time, + name, + ], + value + ); + } + + case SET_HOUSING_TYPE: { + const { time, housingType } = action.payload; + + return state.setIn( + [ + time, + 'housing', + ], + housingType + ); + } + + case SET_PAYS_UTILITY: { + const { time, utility, paysUtility } = action.payload; + + return state.setIn( + [ + time, + utility, + ], + paysUtility + ); + } + + case SET_GETS_FUEL_ASSISTANCE: { + const { time, getsAssistance } = action.payload; + + return state.setIn( + [ + time, + 'fuelAssistance', + ], + getsAssistance + ); + } + + case SET_HAS_BENEFIT: { + const { time, benefit, value } = action.payload; + + state = state.updateIn( + [ + time, + 'benefits', + ], + (benefits) => { + if (value) { + return benefits.add(benefit); + } + else { + return benefits.delete(benefit); + } + } + ); + + // Side effect--might be better if this could be decoupled from the reducer? + if (benefit === 'section8') { + state = state.setIn( + [ + time, + 'housing', + ], + 'voucher' + ); + } + + return state; + } + + default: + return state; + } +}; + +export default clientReducer; diff --git a/src/reducers/geography.js b/src/reducers/geography.js new file mode 100644 index 00000000..64b519d2 --- /dev/null +++ b/src/reducers/geography.js @@ -0,0 +1,23 @@ +import { Map } from 'immutable'; + +import { SET_US_STATE } from '../actions'; + + +/* +State shape: +{ + state: , +} + */ + +const geographyReducer = (state = Map(), action) => { + switch (action.type) { + case SET_US_STATE: { + return state.set('state', action.payload.state); + } + + default: return state; + } +}; + +export default geographyReducer; diff --git a/src/reducers/index.js b/src/reducers/index.js new file mode 100644 index 00000000..6eae2e91 --- /dev/null +++ b/src/reducers/index.js @@ -0,0 +1,17 @@ +/** + * Combine all reducers in this file and export the combined reducers. + */ + +import { combineReducers } from 'redux-immutable'; + +import geographyReducer from './geography'; +import localizationReducer from './localization'; +import clientReducer from './client'; + +export default function createReducer() { + return combineReducers({ + geography: geographyReducer, + localization: localizationReducer, + client: clientReducer, + }); +} diff --git a/src/reducers/localization.js b/src/reducers/localization.js new file mode 100644 index 00000000..f1f68d9c --- /dev/null +++ b/src/reducers/localization.js @@ -0,0 +1,24 @@ +import { Map } from 'immutable'; + +import { SET_LANGUAGE } from '../actions'; + + +/* +State shape: + +{ + currentLanguage: +} + */ + +const localizationReducer = (state = Map(), action) => { + switch (action.type) { + case SET_LANGUAGE: + return state.set('currentLanguage', action.payload.language); + + default: + return state; + } +}; + +export default localizationReducer; diff --git a/src/store.js b/src/store.js new file mode 100644 index 00000000..cfa3eb7f --- /dev/null +++ b/src/store.js @@ -0,0 +1,24 @@ +/** + * Configures the Redux store for use on the browser. Separate from configure-store.js both so + * that configure-store.js could be used server-side (without a DOM reference) and so that other + * client-side modules can get a reference to the store if needed. + */ + +import { Map } from 'immutable'; +import createHistory from 'history/createBrowserHistory'; +import configureStore from './configure-store'; + +export const history = createHistory(); + +const initialState = Map(); + +let store; + +export default function getStore() { + /* istanbul ignore else */ + if (!store) { + store = configureStore(initialState, history); + } + + return store; +} diff --git a/src/test/App.test.js b/src/test/App.test.js index 6dd87c0d..9eb6b8c4 100644 --- a/src/test/App.test.js +++ b/src/test/App.test.js @@ -1,7 +1,7 @@ import React from 'react'; import { shallow } from 'enzyme'; -import App from '../App'; +import App from '../components/App'; global.localStorage = { getItem: function (key) { return `false`; }, @@ -9,6 +9,12 @@ global.localStorage = { setItem: function (key, value) { return; }, }; +const NO_OP = () => {}; + it('renders without crashing', () => { - shallow(); + shallow( + + ); }); diff --git a/src/test/forms/CurrentBenefits.test.js b/src/test/forms/CurrentBenefits.test.js index 965813c9..97f5ae28 100644 --- a/src/test/forms/CurrentBenefits.test.js +++ b/src/test/forms/CurrentBenefits.test.js @@ -3,11 +3,13 @@ import { mount } from 'enzyme'; import { CurrentBenefitsStep } from '../../forms/CurrentBenefits'; -import { CLIENT_DEFAULTS } from '../../utils/CLIENT_DEFAULTS'; - // LOCALIZATION import { getTextForLanguage } from '../../utils/getTextForLanguage'; +import createReducer from '../../reducers'; +import { setUSState } from '../../actions'; + +const reducer = createReducer(); const snippets = getTextForLanguage(`en`); test('Benefits step component should render without error', () => { @@ -16,19 +18,29 @@ test('Benefits step component should render without error', () => { middle: null, right: (
    CB right
    ), }, - updateClientValue = jest.fn(), - saveForm = jest.fn(), askToResetClient = jest.fn(), openFeedback = jest.fn(), benefitsSnippets = snippets.visitPage.currentBenefits; expect(() => { + + const state = reducer( + undefined, + setUSState({ state: 'MA' }) + ); + mount( {} } navData = { navData } - updateClientValue = { updateClientValue } - saveForm = { saveForm } askToResetClient = { askToResetClient } openFeedback = { openFeedback } snippets = { benefitsSnippets } /> diff --git a/src/test/forms/CurrentExpenses.test.js b/src/test/forms/CurrentExpenses.test.js index bed66871..e0d9c7a3 100644 --- a/src/test/forms/CurrentExpenses.test.js +++ b/src/test/forms/CurrentExpenses.test.js @@ -3,11 +3,12 @@ import { mount } from 'enzyme'; import { CurrentExpensesStep } from '../../forms/CurrentExpenses'; -import { CLIENT_DEFAULTS } from '../../utils/CLIENT_DEFAULTS'; - // LOCALIZATION import { getTextForLanguage } from '../../utils/getTextForLanguage'; +import createReducer from '../../reducers'; + +const NO_OP = () => {}; const snippets = getTextForLanguage(`en`); test('Expenses step component should render without error', () => { @@ -16,22 +17,29 @@ test('Expenses step component should render without error', () => { middle: null, right: (
    Exp right
    ), }, - updateClientValue = jest.fn(), - saveForm = jest.fn(), askToResetClient = jest.fn(), openFeedback = jest.fn(), formSnippets = snippets.visitPage.currentExpenses; + + const reducer = createReducer(); + + const state = reducer(undefined, {}); expect(() => { mount( + currentClient = { state.getIn([ + 'client', + 'current', + ]) } + navData = { navData } + askToResetClient = { askToResetClient } + openFeedback = { openFeedback } + setExpenseValue = { NO_OP } + setHousingType = { NO_OP } + setPaysUtility = { NO_OP } + setGetsFuelAssistance = { NO_OP } + snippets = { formSnippets } /> ); }).not.toThrow(); }); diff --git a/src/test/forms/CurrentIncome.test.js b/src/test/forms/CurrentIncome.test.js index 28faad68..1195f7fe 100644 --- a/src/test/forms/CurrentIncome.test.js +++ b/src/test/forms/CurrentIncome.test.js @@ -3,11 +3,11 @@ import { mount } from 'enzyme'; import { CurrentIncomeStep } from '../../forms/CurrentIncome'; -import { CLIENT_DEFAULTS } from '../../utils/CLIENT_DEFAULTS'; - // LOCALIZATION import { getTextForLanguage } from '../../utils/getTextForLanguage'; +import createReducer from '../../reducers'; + const snippets = getTextForLanguage(`en`); test('Income step component should render without error', () => { @@ -16,19 +16,23 @@ test('Income step component should render without error', () => { middle: null, right: (
    Inc right
    ), }, - updateClientValue = jest.fn(), - saveForm = jest.fn(), askToResetClient = jest.fn(), openFeedback = jest.fn(), formSnippets = snippets.visitPage.currentIncome; expect(() => { + const reducer = createReducer(); + + const state = reducer(undefined, {}); + mount( {} } navData = { navData } - updateClientValue = { updateClientValue } - saveForm = { saveForm } askToResetClient = { askToResetClient } openFeedback = { openFeedback } snippets = { formSnippets } /> diff --git a/src/test/forms/ExpensesOther.test.js b/src/test/forms/ExpensesOther.test.js deleted file mode 100644 index fe994cb0..00000000 --- a/src/test/forms/ExpensesOther.test.js +++ /dev/null @@ -1,76 +0,0 @@ -import React from 'react'; -import { mount } from 'enzyme'; -import { defaultsDeep } from 'lodash'; - -import { ExpensesOther } from '../../forms/ExpensesOther'; -import { CLIENT_DEFAULTS } from '../../utils/CLIENT_DEFAULTS'; - -const GREATER_THAN_ZERO = 1; - -const displayRow = (page, generic) => { - return page.find(`CashFlowDisplayRow[generic="${generic}"]`); -}; -const inputRow = (page, generic) => { - return page.find(`CashFlowInputsRow[generic="${generic}"]`); -}; - -describe('', () => { - const defaultProps = { - time: 'monthly', - updateClientValue: jest.fn(), - }; - - const buildPage = (clientValues = {}) => { - const client = defaultsDeep({}, clientValues, CLIENT_DEFAULTS.current); - return mount(); - }; - - it('renders housing costs control when related costs', () => { - const homeless = { housing: 'homeless' }; // no housing costs - expect(displayRow(buildPage(homeless), 'housingCosts')).toHaveLength(0); - - const renter = { housing: 'renter', rent: GREATER_THAN_ZERO }; - expect(displayRow(buildPage(renter), 'housingCosts')).toHaveLength(1); - }); - - it('renders dependent care control when costs unrelated to transportation', () => { - const transportCosts = { childTransportation: GREATER_THAN_ZERO }; - expect(displayRow(buildPage(transportCosts), 'dependentCare')).toHaveLength(0); - - const childCare = { childDirectCare: GREATER_THAN_ZERO }; - expect(displayRow(buildPage(childCare), 'dependentCare')).toHaveLength(1); - }); - - it('renders dependent transport control when related costs', () => { - const childCare = { childDirectCare: GREATER_THAN_ZERO }; - expect(displayRow(buildPage(childCare), 'dependentTransport')).toHaveLength(0); - - const transportCosts = { childTransportation: GREATER_THAN_ZERO }; - expect(displayRow(buildPage(transportCosts), 'dependentTransport')).toHaveLength(1); - }); - - it('renders medical costs control when related costs', () => { - expect(displayRow(buildPage(), 'medicalTotal')).toHaveLength(0); - - const medicalCosts = { otherMedical: GREATER_THAN_ZERO }; - expect(displayRow(buildPage(medicalCosts), 'medicalTotal')).toHaveLength(1); - }); - - it('renders cable and internet costs control when not homeless', () => { - const homeless = { housing: 'homeless' }; - expect(inputRow(buildPage(homeless), 'otherExpensesCable')).toHaveLength(0); - - const renter = { housing: 'renter' }; - expect(inputRow(buildPage(renter), 'otherExpensesCable')).toHaveLength(1); - }); - - it('renders utilities costs control when not homeless', () => { - const homeless = { housing: 'homeless' }; - expect(inputRow(buildPage(homeless), 'otherExpensesUtilities')).toHaveLength(0); - - const renter = { housing: 'renter' }; - expect(inputRow(buildPage(renter), 'otherExpensesUtilities')).toHaveLength(1); - }); -}); diff --git a/src/test/forms/Household.test.js b/src/test/forms/Household.test.js index 4d0d4718..5e9d00be 100644 --- a/src/test/forms/Household.test.js +++ b/src/test/forms/Household.test.js @@ -3,11 +3,13 @@ import { mount } from 'enzyme'; import { HouseholdStep } from '../../forms/Household'; -import { CLIENT_DEFAULTS } from '../../utils/CLIENT_DEFAULTS'; - // LOCALIZATION import { getTextForLanguage } from '../../utils/getTextForLanguage'; +import createReducer from '../../reducers'; + +const NO_OP = () => {}; + const snippets = getTextForLanguage(`en`); test('Household step component should render without error', () => { @@ -16,22 +18,33 @@ test('Household step component should render without error', () => { middle: null, right: (
    House right
    ), }, - updateClientValue = jest.fn(), saveForm = jest.fn(), askToResetClient = jest.fn(), openFeedback = jest.fn(), formSnippets = snippets.visitPage.household; + + const reducer = createReducer(); + + const state = reducer(undefined, {}); expect(() => { mount( + household = { state.getIn([ + 'client', + 'current', + 'household', + ]) } + setMemberAge = { NO_OP } + setMemberIsDisabled = { NO_OP } + setMemberRole = { NO_OP } + removeMember = { NO_OP } + addMember = { NO_OP } + navData = { navData } + saveForm = { saveForm } + askToResetClient = { askToResetClient } + openFeedback = { openFeedback } + snippets = { formSnippets } /> ); }).not.toThrow(); }); diff --git a/src/test/forms/cashflow/CashFlowDisplayRow.test.js b/src/test/forms/cashflow/CashFlowDisplayRow.test.js index fcad3101..2412b750 100644 --- a/src/test/forms/cashflow/CashFlowDisplayRow.test.js +++ b/src/test/forms/cashflow/CashFlowDisplayRow.test.js @@ -3,6 +3,11 @@ import { mount } from 'enzyme'; import { CashFlowDisplayRow } from '../../../forms/cashflow'; +import createReducer from '../../../reducers'; +import { setCashValue } from '../../../actions'; + +const reducer = createReducer(); + describe('', () => { it('renders using provided value', () => { const propsWithValue = { @@ -14,10 +19,22 @@ describe('', () => { }); it('renders using value in timeState', () => { + const state = reducer( + undefined, + setCashValue({ + time: 'current', + name: 'potatoes', + value: 15, + }) + ); + const propsWithTimeState = { generic: 'potatoes', - timeState: { potatoes: 15 }, - children: More potatoes, huh?, + timeState: state.getIn([ + 'client', + 'current', + ]), + children: More potatoes, huh?, }; expect(mount()).toMatchSnapshot(); }); diff --git a/src/test/forms/cashflow/CashFlowInputsRow.test.js b/src/test/forms/cashflow/CashFlowInputsRow.test.js index cd9248b5..beb3f5b3 100644 --- a/src/test/forms/cashflow/CashFlowInputsRow.test.js +++ b/src/test/forms/cashflow/CashFlowInputsRow.test.js @@ -1,4 +1,5 @@ import React from 'react'; +import { fromJS } from 'immutable'; import { mount, shallow } from 'enzyme'; import { CashFlowInputsRow } from '../../../forms/cashflow'; @@ -14,20 +15,20 @@ test('CashFlowInputsRow should render', () => { mount( {} } > + timeState={ fromJS({ [ propName ]: 0 }) } + setValue={ () => {} } > label ); }).not.toThrow(); }); -test(`Second ManagedNumberField child should have value of timeState[ 'clientProp' ]`, () => { +test(`Second ManagedNumberField child should have value of timeState.get(clientProp)`, () => { const wrapper = mount( {} }> + timeState={ fromJS({ [ propName ]: monthlyVal }) } + setValue={ () => {} }> label ); @@ -39,8 +40,8 @@ test('First ManagedNumberField child should have weekly value', () => { const wrapper = mount( {} }> + timeState={ fromJS({ [ propName ]: monthlyVal }) } + setValue={ () => {} }> label ); @@ -52,8 +53,8 @@ test('Third ManagedNumberField child should have yearly value', () => { const wrapper = mount( {} }> + timeState={ fromJS({ [ propName ]: monthlyVal }) } + setValue={ () => {} }> label ); @@ -61,13 +62,13 @@ test('Third ManagedNumberField child should have yearly value', () => { expect(yearlyInput.prop(`value`)).toBeCloseTo(monthlyVal * 12); }); -test('updateClientValue gets called correctly when each value is changed', () => { +test('setValue gets called correctly when each value is changed', () => { const mockSetClientProperty = jest.fn(); const wrapper = shallow( + timeState={ fromJS({ [ propName ]: monthlyVal }) } + setValue={ mockSetClientProperty }> label ); @@ -85,8 +86,7 @@ test('updateClientValue gets called correctly when each value is changed', () => MNF.prop('onChange')(evnt, { value: newValue }); expect(mockSetClientProperty.mock.calls).toHaveLength(i + 1); - expect(mockSetClientProperty.mock.calls[ i ][ 0 ]).toBe(evnt); - expect(mockSetClientProperty.mock.calls[ i ][ 1 ].name).toBe(propName) ; - expect(mockSetClientProperty.mock.calls[ i ][ 1 ].value).toBeCloseTo(newValue * multipliers[ i ]); + expect(mockSetClientProperty.mock.calls[ i ][ 0 ].name).toBe(propName) ; + expect(mockSetClientProperty.mock.calls[ i ][ 0 ].value).toBeCloseTo(newValue * multipliers[ i ]); } }); diff --git a/src/test/forms/cashflow/__snapshots__/CashFlowDisplayRow.test.js.snap b/src/test/forms/cashflow/__snapshots__/CashFlowDisplayRow.test.js.snap index 4cd6ecce..8b4b77b0 100644 --- a/src/test/forms/cashflow/__snapshots__/CashFlowDisplayRow.test.js.snap +++ b/src/test/forms/cashflow/__snapshots__/CashFlowDisplayRow.test.js.snap @@ -40,8 +40,63 @@ exports[` renders using value in timeState 1`] = ` diff --git a/src/test/forms/inputs/BigButton.test.js b/src/test/forms/inputs/BigButton.test.js index 470ce139..7866bcff 100644 --- a/src/test/forms/inputs/BigButton.test.js +++ b/src/test/forms/inputs/BigButton.test.js @@ -6,6 +6,7 @@ import { BigButton } from '../../../forms/inputs'; describe('', () => { it('renders Button with overridden props', () => { const child = Click me!; + const button = mount( { const LineMock = () => { @@ -19,26 +22,27 @@ jest.mock('react-chartjs-2', () => { describe('', () => { let activePrograms; - let client; let defaultProps; - const buildGraph = () => { - return mount(); + const buildGraph = (state) => { + return mount(); }; beforeEach(() => { activePrograms = []; - client = cloneDeep(CLIENT_DEFAULTS); defaultProps = { activePrograms: activePrograms, - client: client, timescale: 'Monthly', className: 'some-class', }; }); it('renders message when no benefits selected', () => { - const graph = buildGraph(); + const state = reducer(undefined, {}); + + const graph = buildGraph(state); expect(graph.children).toHaveLength(1); expect(graph.childAt(0).is('Message')).toBe(true); @@ -46,34 +50,112 @@ describe('', () => { it('renders with snap and current earned less than future earned', () => { activePrograms.push('snap'); - set(client, 'current.earned', 100); - set(client, 'future.earned', 200); - expect(buildGraph()).toMatchSnapshot(); + const state = [ + setHasBenefit({ + time: 'current', + benefit: 'snap', + value: true, + }), + + setCashValue({ + time: 'current', + name: 'earned', + value: 100, + }), + + setCashValue({ + time: 'future', + name: 'earned', + value: 200, + }), + ].reduce(reducer, undefined); + + expect(buildGraph(state)).toMatchSnapshot(); }); it('renders with snap and current earned greater than future earned', () => { activePrograms.push('snap'); - set(client, 'current.earned', 200); - set(client, 'future.earned', 100); - - expect(buildGraph()).toMatchSnapshot(); + + const state = [ + setHasBenefit({ + time: 'current', + benefit: 'snap', + value: true, + }), + + setCashValue({ + time: 'current', + name: 'earned', + value: 200, + }), + + setCashValue({ + time: 'future', + name: 'earned', + value: 100, + }), + ].reduce(reducer, undefined); + + expect(buildGraph(state)).toMatchSnapshot(); }); it('renders with section8', () => { activePrograms.push('section8'); - set(client, 'current.earned', 100); - set(client, 'future.earned', 200); - expect(buildGraph()).toMatchSnapshot(); + const state = [ + setHasBenefit({ + time: 'current', + benefit: 'section8', + value: true, + }), + + setCashValue({ + time: 'current', + name: 'earned', + value: 100, + }), + + setCashValue({ + time: 'future', + name: 'earned', + value: 200, + }), + ].reduce(reducer, undefined); + + expect(buildGraph(state)).toMatchSnapshot(); }); it('renders with both snap and section8', () => { activePrograms.push('snap'); activePrograms.push('section8'); - set(client, 'current.earned', 100); - set(client, 'future.earned', 200); - expect(buildGraph()).toMatchSnapshot(); + const state = [ + setHasBenefit({ + time: 'current', + benefit: 'snap', + value: true, + }), + + setHasBenefit({ + time: 'current', + benefit: 'section8', + value: true, + }), + + setCashValue({ + time: 'current', + name: 'earned', + value: 100, + }), + + setCashValue({ + time: 'future', + name: 'earned', + value: 200, + }), + ].reduce(reducer, undefined); + + expect(buildGraph(state)).toMatchSnapshot(); }); }); diff --git a/src/test/forms/output/BenefitsTable.test.js b/src/test/forms/output/BenefitsTable.test.js index 7c842b6e..4cb0cc18 100644 --- a/src/test/forms/output/BenefitsTable.test.js +++ b/src/test/forms/output/BenefitsTable.test.js @@ -1,15 +1,18 @@ import React from 'react'; import renderer from 'react-test-renderer'; -import { cloneDeep, set } from 'lodash'; import { BenefitsTable } from '../../../forms/output/BenefitsTable'; import { snippets } from '../../helpers'; -import { CLIENT_DEFAULTS } from '../../../utils/CLIENT_DEFAULTS'; -const buildSnapshot = (client) => { +import createReducer from '../../../reducers'; +import { setHasBenefit, setCashValue } from '../../../actions'; + +const reducer = createReducer(); + +const buildSnapshot = (state) => { const rendered = renderer.create( ); @@ -17,23 +20,66 @@ const buildSnapshot = (client) => { }; test('Benefits table renders correctly', () => { - const client = cloneDeep(CLIENT_DEFAULTS); - expect(buildSnapshot(client)).toMatchSnapshot(); + let state = reducer(undefined, {}); - const benefits = [ 'snap' ]; + expect(buildSnapshot(state)).toMatchSnapshot(); - set(client, 'current.benefits', benefits); - expect(buildSnapshot(client)).toMatchSnapshot(); + state = [ + setHasBenefit({ + time: 'current', + benefit: 'snap', + value: true, + }), + ].reduce(reducer, state); - set(client, 'current.earned', 100); - expect(buildSnapshot(client)).toMatchSnapshot(); + state = reducer( + state, + setHasBenefit({ + time: 'current', + benefit: 'snap', + value: true, + }) + ); - set(client, 'future.earned', 200); - expect(buildSnapshot(client)).toMatchSnapshot(); + expect(buildSnapshot(state)).toMatchSnapshot(); - set(client, 'current.earned', 300); - expect(buildSnapshot(client)).toMatchSnapshot(); + state = reducer( + state, + setCashValue({ + time: 'current', + name: 'earned', + value: 100, + }) + ); + expect(buildSnapshot(state)).toMatchSnapshot(); - set(client, 'future.earned', 400); - expect(buildSnapshot(client)).toMatchSnapshot(); + state = reducer( + state, + setCashValue({ + time: 'future', + name: 'earned', + value: 200, + }) + ); + expect(buildSnapshot(state)).toMatchSnapshot(); + + state = reducer( + state, + setCashValue({ + time: 'current', + name: 'earned', + value: 300, + }) + ); + expect(buildSnapshot(state)).toMatchSnapshot(); + + state = reducer( + state, + setCashValue({ + time: 'future', + name: 'earned', + value: 400, + }) + ); + expect(buildSnapshot(state)).toMatchSnapshot(); }); diff --git a/src/test/forms/output/StackedAreaGraph.test.js b/src/test/forms/output/StackedAreaGraph.test.js index 0e811d81..405f83d2 100644 --- a/src/test/forms/output/StackedAreaGraph.test.js +++ b/src/test/forms/output/StackedAreaGraph.test.js @@ -1,9 +1,12 @@ import React from 'react'; import { mount } from 'enzyme'; -import { cloneDeep, set } from 'lodash'; import { StackedAreaGraph } from '../../../forms/output/StackedAreaGraph'; -import { CLIENT_DEFAULTS } from '../../../utils/CLIENT_DEFAULTS'; + +import createReducer from '../../../reducers'; +import { setCashValue } from '../../../actions'; + +const reducer = createReducer(); jest.mock('react-chartjs-2', () => { const LineMock = () => { @@ -19,53 +22,99 @@ jest.mock('react-chartjs-2', () => { describe('', () => { let activePrograms; - let client; let defaultProps; - const buildGraph = () => { - return mount(); + const buildGraph = (state) => { + return mount(); }; beforeEach(() => { activePrograms = []; - client = cloneDeep(CLIENT_DEFAULTS); defaultProps = { activePrograms: activePrograms, - client: client, timescale: 'Monthly', }; }); it('renders with snap and current earned less than future earned', () => { activePrograms.push('snap'); - set(client, 'current.earned', 100); - set(client, 'future.earned', 200); - expect(buildGraph()).toMatchSnapshot(); + const state = [ + setCashValue({ + time: 'current', + name: 'earned', + value: 100, + }), + + setCashValue({ + time: 'future', + name: 'earned', + value: 200, + }), + ].reduce(reducer, undefined); + + expect(buildGraph(state)).toMatchSnapshot(); }); it('renders with snap and current earned greater than future earned', () => { activePrograms.push('snap'); - set(client, 'current.earned', 200); - set(client, 'future.earned', 100); - - expect(buildGraph()).toMatchSnapshot(); + + const state = [ + setCashValue({ + time: 'current', + name: 'earned', + value: 200, + }), + + setCashValue({ + time: 'future', + name: 'earned', + value: 100, + }), + ].reduce(reducer, undefined); + + expect(buildGraph(state)).toMatchSnapshot(); }); it('renders with section8', () => { activePrograms.push('section8'); - set(client, 'current.earned', 100); - set(client, 'future.earned', 200); - expect(buildGraph()).toMatchSnapshot(); + const state = [ + setCashValue({ + time: 'current', + name: 'earned', + value: 100, + }), + + setCashValue({ + time: 'future', + name: 'earned', + value: 200, + }), + ].reduce(reducer, undefined); + + expect(buildGraph(state)).toMatchSnapshot(); }); it('renders with both snap and section8', () => { - activePrograms.push('snap'); - activePrograms.push('section8'); - set(client, 'current.earned', 100); - set(client, 'future.earned', 200); + activePrograms.push('snap', 'section8'); + + const state = [ + setCashValue({ + time: 'current', + name: 'earned', + value: 100, + }), + + setCashValue({ + time: 'future', + name: 'earned', + value: 200, + }), + ].reduce(reducer, undefined); - expect(buildGraph()).toMatchSnapshot(); + expect(buildGraph(state)).toMatchSnapshot(); }); }); diff --git a/src/test/forms/output/StackedBarGraph.test.js b/src/test/forms/output/StackedBarGraph.test.js index 2d7b179b..4592b075 100644 --- a/src/test/forms/output/StackedBarGraph.test.js +++ b/src/test/forms/output/StackedBarGraph.test.js @@ -1,9 +1,12 @@ import React from 'react'; import { mount } from 'enzyme'; -import { cloneDeep, set } from 'lodash'; import { StackedBarGraph } from '../../../forms/output/StackedBarGraph'; -import { CLIENT_DEFAULTS } from '../../../utils/CLIENT_DEFAULTS'; + +import createReducer from '../../../reducers'; +import { setHasBenefit, setCashValue } from '../../../actions'; + +const reducer = createReducer(); jest.mock('react-chartjs-2', () => { const BarMock = () => { @@ -18,56 +21,131 @@ jest.mock('react-chartjs-2', () => { }); describe('', () => { - let client; - - const buildGraph = () => { - return mount(); + const buildGraph = (state) => { + return mount(); }; - beforeEach(() => { - client = cloneDeep(CLIENT_DEFAULTS); - }); - it('renders with snap and current earned less than future earned', () => { - const benefits = [ 'snap' ]; - - set(client, 'current.benefits', benefits); - set(client, 'current.earned', 100); - set(client, 'future.earned', 200); - - expect(buildGraph()).toMatchSnapshot(); + /* + * Break down what's happening here; we're applying a series of actions through the + * reducer to create a state object. This is essentially what Redux does through dispatch(). + * The way a reducer function works (not unique to Redux) is that it takes at leat two arguments: + * + * function reducer(currentState, iteratorValue) { + * return changeCurrentState(currentState, iteratorValue); + * } + * + * Each time through the loop, the function takes the return value of the previous iteration, and the + * value being iterated, and returns some value, until it's done iterating, at which point it returns + * the final return value. Since we're iterating over an array of actions, each time through the loop + * the second argument will be an action. The reducer then takes that action, and the previous state, + * and returns a new state. At the end of the iteration, we'll have a state object exactly as is Redux + * had dispatched each of these actions in succession. + */ + + const state = [ + setHasBenefit({ + time: 'current', + benefit: 'snap', + value: true, + }), + + setCashValue({ + time: 'current', + name: 'earned', + value: 100, + }), + + setCashValue({ + time: 'future', + name: 'earned', + value: 200, + }), + ].reduce( + reducer, + // Since we don't have an initial value, we have to pass `undefined` here; otherwise Javascript's + // Array.reduce() uses the object being iterated over (in this case, the array of actions) as the initial value. + undefined + ); + + expect(buildGraph(state)).toMatchSnapshot(); }); it('renders with snap and current earned greater than future earned', () => { - const benefits = [ 'snap' ]; - - set(client, 'current.benefits', benefits); - set(client, 'current.earned', 200); - set(client, 'future.earned', 100); - - expect(buildGraph()).toMatchSnapshot(); + const state = [ + setHasBenefit({ + time: 'current', + benefit: 'snap', + value: true, + }), + + setCashValue({ + time: 'current', + name: 'earned', + value: 200, + }), + + setCashValue({ + time: 'future', + name: 'earned', + value: 100, + }), + ].reduce(reducer, undefined); + + expect(buildGraph(state)).toMatchSnapshot(); }); it('renders with section8', () => { - const benefits = [ 'section8' ]; - - set(client, 'current.benefits', benefits); - set(client, 'current.earned', 100); - set(client, 'future.earned', 200); - - expect(buildGraph()).toMatchSnapshot(); + const state = [ + setHasBenefit({ + time: 'current', + benefit: 'section8', + value: true, + }), + + setCashValue({ + time: 'current', + name: 'earned', + value: 100, + }), + + setCashValue({ + time: 'future', + name: 'earned', + value: 200, + }), + ].reduce(reducer, undefined); + + expect(buildGraph(state)).toMatchSnapshot(); }); it('renders with both snap and section8', () => { - const benefits = [ - 'snap', - 'section8', - ]; - - set(client, 'current.benefits', benefits); - set(client, 'current.earned', 100); - set(client, 'future.earned', 200); - - expect(buildGraph()).toMatchSnapshot(); + const state = [ + setHasBenefit({ + time: 'current', + benefit: 'snap', + value: true, + }), + + setHasBenefit({ + time: 'current', + benefit: 'section8', + value: true, + }), + + setCashValue({ + time: 'current', + name: 'earned', + value: 100, + }), + + setCashValue({ + time: 'future', + name: 'earned', + value: 200, + }), + ].reduce(reducer, undefined); + + expect(buildGraph(state)).toMatchSnapshot(); }); }); diff --git a/src/test/forms/output/__snapshots__/BenefitsLineGraph.test.js.snap b/src/test/forms/output/__snapshots__/BenefitsLineGraph.test.js.snap index 3180e9b7..184eaa05 100644 --- a/src/test/forms/output/__snapshots__/BenefitsLineGraph.test.js.snap +++ b/src/test/forms/output/__snapshots__/BenefitsLineGraph.test.js.snap @@ -10,119 +10,123 @@ exports[` renders with both snap and section8 1`] = ` } className="some-class" client={ - Object { - "USState": "MA", - "current": Object { - "SSDI": 0, - "SSI": 0, - "TAFDC": 0, - "adultDirectCare": 0, - "adultOtherCare": 0, - "adultTransportation": 0, - "alimony": 0, - "benefits": Array [], - "childBeforeAndAfterSchoolCare": 0, - "childDirectCare": 0, - "childOtherCare": 0, - "childSupportIn": 0, - "childSupportPaidOut": 0, - "childTransportation": 0, - "climateControl": false, - "contractRent": 0, - "disabledAssistance": 0, - "disabledMedical": 0, - "earned": 100, - "earnedBecauseOfAdultCare": 0, - "earnedBecauseOfChildCare": 0, - "fuelAssistance": false, - "household": Array [ - Object { - "m_age": 30, - "m_disabled": false, - "m_role": "head", + Immutable.Map { + USState: "MA", + current: Immutable.Map { + otherExpensesCareProducts: 0, + otherMedical: 0, + otherExpensesPhone: 0, + workersComp: 0, + childOtherCare: 0, + earnedBecauseOfAdultCare: 0, + SSDI: 0, + unemployment: 0, + otherExpensesOther: 0, + childBeforeAndAfterSchoolCare: 0, + rentShare: 0, + otherExpensesMedical: 0, + adultTransportation: 0, + adultOtherCare: 0, + earnedBecauseOfChildCare: 0, + childTransportation: 0, + otherExpensesFood: 0, + SSI: 0, + alimony: 0, + contractRent: 0, + socialSecurity: 0, + phone: false, + fuelAssistance: false, + pension: 0, + childSupportPaidOut: 0, + otherExpensesCable: 0, + disabledAssistance: 0, + nonHeatElectricity: false, + adultDirectCare: 0, + disabledMedical: 0, + childDirectCare: 0, + propertyTax: 0, + wantsToSeeOtherExpenses: false, + earned: 100, + climateControl: false, + TAFDC: 0, + childSupportIn: 0, + otherIncome: 0, + rent: 0, + mortgage: 0, + benefits: Immutable.Set [ + "snap", + "section8", + ], + otherExpensesEntertainment: 0, + housingInsurance: 0, + otherExpensesClothes: 0, + housing: "voucher", + otherExpensesTransport: 0, + otherExpensesUtilities: 0, + household: Immutable.List [ + Immutable.Map { + m_age: 30, + m_role: "head", + m_disabled: false, }, ], - "housing": "homeless", - "housingInsurance": 0, - "mortgage": 0, - "nonHeatElectricity": false, - "otherExpensesCable": 0, - "otherExpensesCareProducts": 0, - "otherExpensesClothes": 0, - "otherExpensesEntertainment": 0, - "otherExpensesFood": 0, - "otherExpensesMedical": 0, - "otherExpensesOther": 0, - "otherExpensesPhone": 0, - "otherExpensesTransport": 0, - "otherExpensesUtilities": 0, - "otherIncome": 0, - "otherMedical": 0, - "pension": 0, - "phone": false, - "propertyTax": 0, - "rent": 0, - "rentShare": 0, - "socialSecurity": 0, - "unemployment": 0, - "wantsToSeeOtherExpenses": false, - "workersComp": 0, }, - "future": Object { - "SSDI": 0, - "SSI": 0, - "TAFDC": 0, - "adultDirectCare": 0, - "adultOtherCare": 0, - "adultTransportation": 0, - "alimony": 0, - "benefits": Array [], - "childBeforeAndAfterSchoolCare": 0, - "childDirectCare": 0, - "childOtherCare": 0, - "childSupportIn": 0, - "childSupportPaidOut": 0, - "childTransportation": 0, - "climateControl": false, - "contractRent": 0, - "disabledAssistance": 0, - "disabledMedical": 0, - "earned": 200, - "earnedBecauseOfAdultCare": 0, - "earnedBecauseOfChildCare": 0, - "fuelAssistance": false, - "household": Array [ - Object { - "m_age": 30, - "m_disabled": false, - "m_role": "head", + future: Immutable.Map { + otherExpensesCareProducts: 0, + otherMedical: 0, + otherExpensesPhone: 0, + workersComp: 0, + childOtherCare: 0, + earnedBecauseOfAdultCare: 0, + SSDI: 0, + unemployment: 0, + otherExpensesOther: 0, + childBeforeAndAfterSchoolCare: 0, + rentShare: 0, + otherExpensesMedical: 0, + adultTransportation: 0, + adultOtherCare: 0, + earnedBecauseOfChildCare: 0, + childTransportation: 0, + otherExpensesFood: 0, + SSI: 0, + alimony: 0, + contractRent: 0, + socialSecurity: 0, + phone: false, + fuelAssistance: false, + pension: 0, + childSupportPaidOut: 0, + otherExpensesCable: 0, + disabledAssistance: 0, + nonHeatElectricity: false, + adultDirectCare: 0, + disabledMedical: 0, + childDirectCare: 0, + propertyTax: 0, + wantsToSeeOtherExpenses: false, + earned: 200, + climateControl: false, + TAFDC: 0, + childSupportIn: 0, + otherIncome: 0, + rent: 0, + mortgage: 0, + benefits: Immutable.Set [ + ], + otherExpensesEntertainment: 0, + housingInsurance: 0, + otherExpensesClothes: 0, + housing: "homeless", + otherExpensesTransport: 0, + otherExpensesUtilities: 0, + household: Immutable.List [ + Immutable.Map { + m_age: 30, + m_role: "head", + m_disabled: false, }, ], - "housing": "homeless", - "housingInsurance": 0, - "mortgage": 0, - "nonHeatElectricity": false, - "otherExpensesCable": 0, - "otherExpensesCareProducts": 0, - "otherExpensesClothes": 0, - "otherExpensesEntertainment": 0, - "otherExpensesFood": 0, - "otherExpensesMedical": 0, - "otherExpensesOther": 0, - "otherExpensesPhone": 0, - "otherExpensesTransport": 0, - "otherExpensesUtilities": 0, - "otherIncome": 0, - "otherMedical": 0, - "pension": 0, - "phone": false, - "propertyTax": 0, - "rent": 0, - "rentShare": 0, - "socialSecurity": 0, - "unemployment": 0, - "wantsToSeeOtherExpenses": false, - "workersComp": 0, }, } } @@ -1045,119 +1049,122 @@ exports[` renders with section8 1`] = ` } className="some-class" client={ - Object { - "USState": "MA", - "current": Object { - "SSDI": 0, - "SSI": 0, - "TAFDC": 0, - "adultDirectCare": 0, - "adultOtherCare": 0, - "adultTransportation": 0, - "alimony": 0, - "benefits": Array [], - "childBeforeAndAfterSchoolCare": 0, - "childDirectCare": 0, - "childOtherCare": 0, - "childSupportIn": 0, - "childSupportPaidOut": 0, - "childTransportation": 0, - "climateControl": false, - "contractRent": 0, - "disabledAssistance": 0, - "disabledMedical": 0, - "earned": 100, - "earnedBecauseOfAdultCare": 0, - "earnedBecauseOfChildCare": 0, - "fuelAssistance": false, - "household": Array [ - Object { - "m_age": 30, - "m_disabled": false, - "m_role": "head", + Immutable.Map { + USState: "MA", + current: Immutable.Map { + otherExpensesCareProducts: 0, + otherMedical: 0, + otherExpensesPhone: 0, + workersComp: 0, + childOtherCare: 0, + earnedBecauseOfAdultCare: 0, + SSDI: 0, + unemployment: 0, + otherExpensesOther: 0, + childBeforeAndAfterSchoolCare: 0, + rentShare: 0, + otherExpensesMedical: 0, + adultTransportation: 0, + adultOtherCare: 0, + earnedBecauseOfChildCare: 0, + childTransportation: 0, + otherExpensesFood: 0, + SSI: 0, + alimony: 0, + contractRent: 0, + socialSecurity: 0, + phone: false, + fuelAssistance: false, + pension: 0, + childSupportPaidOut: 0, + otherExpensesCable: 0, + disabledAssistance: 0, + nonHeatElectricity: false, + adultDirectCare: 0, + disabledMedical: 0, + childDirectCare: 0, + propertyTax: 0, + wantsToSeeOtherExpenses: false, + earned: 100, + climateControl: false, + TAFDC: 0, + childSupportIn: 0, + otherIncome: 0, + rent: 0, + mortgage: 0, + benefits: Immutable.Set [ + "section8", + ], + otherExpensesEntertainment: 0, + housingInsurance: 0, + otherExpensesClothes: 0, + housing: "voucher", + otherExpensesTransport: 0, + otherExpensesUtilities: 0, + household: Immutable.List [ + Immutable.Map { + m_age: 30, + m_role: "head", + m_disabled: false, }, ], - "housing": "homeless", - "housingInsurance": 0, - "mortgage": 0, - "nonHeatElectricity": false, - "otherExpensesCable": 0, - "otherExpensesCareProducts": 0, - "otherExpensesClothes": 0, - "otherExpensesEntertainment": 0, - "otherExpensesFood": 0, - "otherExpensesMedical": 0, - "otherExpensesOther": 0, - "otherExpensesPhone": 0, - "otherExpensesTransport": 0, - "otherExpensesUtilities": 0, - "otherIncome": 0, - "otherMedical": 0, - "pension": 0, - "phone": false, - "propertyTax": 0, - "rent": 0, - "rentShare": 0, - "socialSecurity": 0, - "unemployment": 0, - "wantsToSeeOtherExpenses": false, - "workersComp": 0, }, - "future": Object { - "SSDI": 0, - "SSI": 0, - "TAFDC": 0, - "adultDirectCare": 0, - "adultOtherCare": 0, - "adultTransportation": 0, - "alimony": 0, - "benefits": Array [], - "childBeforeAndAfterSchoolCare": 0, - "childDirectCare": 0, - "childOtherCare": 0, - "childSupportIn": 0, - "childSupportPaidOut": 0, - "childTransportation": 0, - "climateControl": false, - "contractRent": 0, - "disabledAssistance": 0, - "disabledMedical": 0, - "earned": 200, - "earnedBecauseOfAdultCare": 0, - "earnedBecauseOfChildCare": 0, - "fuelAssistance": false, - "household": Array [ - Object { - "m_age": 30, - "m_disabled": false, - "m_role": "head", + future: Immutable.Map { + otherExpensesCareProducts: 0, + otherMedical: 0, + otherExpensesPhone: 0, + workersComp: 0, + childOtherCare: 0, + earnedBecauseOfAdultCare: 0, + SSDI: 0, + unemployment: 0, + otherExpensesOther: 0, + childBeforeAndAfterSchoolCare: 0, + rentShare: 0, + otherExpensesMedical: 0, + adultTransportation: 0, + adultOtherCare: 0, + earnedBecauseOfChildCare: 0, + childTransportation: 0, + otherExpensesFood: 0, + SSI: 0, + alimony: 0, + contractRent: 0, + socialSecurity: 0, + phone: false, + fuelAssistance: false, + pension: 0, + childSupportPaidOut: 0, + otherExpensesCable: 0, + disabledAssistance: 0, + nonHeatElectricity: false, + adultDirectCare: 0, + disabledMedical: 0, + childDirectCare: 0, + propertyTax: 0, + wantsToSeeOtherExpenses: false, + earned: 200, + climateControl: false, + TAFDC: 0, + childSupportIn: 0, + otherIncome: 0, + rent: 0, + mortgage: 0, + benefits: Immutable.Set [ + ], + otherExpensesEntertainment: 0, + housingInsurance: 0, + otherExpensesClothes: 0, + housing: "homeless", + otherExpensesTransport: 0, + otherExpensesUtilities: 0, + household: Immutable.List [ + Immutable.Map { + m_age: 30, + m_role: "head", + m_disabled: false, }, ], - "housing": "homeless", - "housingInsurance": 0, - "mortgage": 0, - "nonHeatElectricity": false, - "otherExpensesCable": 0, - "otherExpensesCareProducts": 0, - "otherExpensesClothes": 0, - "otherExpensesEntertainment": 0, - "otherExpensesFood": 0, - "otherExpensesMedical": 0, - "otherExpensesOther": 0, - "otherExpensesPhone": 0, - "otherExpensesTransport": 0, - "otherExpensesUtilities": 0, - "otherIncome": 0, - "otherMedical": 0, - "pension": 0, - "phone": false, - "propertyTax": 0, - "rent": 0, - "rentShare": 0, - "socialSecurity": 0, - "unemployment": 0, - "wantsToSeeOtherExpenses": false, - "workersComp": 0, }, } } @@ -1876,119 +1883,122 @@ exports[` renders with snap and current earned greater than f } className="some-class" client={ - Object { - "USState": "MA", - "current": Object { - "SSDI": 0, - "SSI": 0, - "TAFDC": 0, - "adultDirectCare": 0, - "adultOtherCare": 0, - "adultTransportation": 0, - "alimony": 0, - "benefits": Array [], - "childBeforeAndAfterSchoolCare": 0, - "childDirectCare": 0, - "childOtherCare": 0, - "childSupportIn": 0, - "childSupportPaidOut": 0, - "childTransportation": 0, - "climateControl": false, - "contractRent": 0, - "disabledAssistance": 0, - "disabledMedical": 0, - "earned": 200, - "earnedBecauseOfAdultCare": 0, - "earnedBecauseOfChildCare": 0, - "fuelAssistance": false, - "household": Array [ - Object { - "m_age": 30, - "m_disabled": false, - "m_role": "head", + Immutable.Map { + USState: "MA", + current: Immutable.Map { + otherExpensesCareProducts: 0, + otherMedical: 0, + otherExpensesPhone: 0, + workersComp: 0, + childOtherCare: 0, + earnedBecauseOfAdultCare: 0, + SSDI: 0, + unemployment: 0, + otherExpensesOther: 0, + childBeforeAndAfterSchoolCare: 0, + rentShare: 0, + otherExpensesMedical: 0, + adultTransportation: 0, + adultOtherCare: 0, + earnedBecauseOfChildCare: 0, + childTransportation: 0, + otherExpensesFood: 0, + SSI: 0, + alimony: 0, + contractRent: 0, + socialSecurity: 0, + phone: false, + fuelAssistance: false, + pension: 0, + childSupportPaidOut: 0, + otherExpensesCable: 0, + disabledAssistance: 0, + nonHeatElectricity: false, + adultDirectCare: 0, + disabledMedical: 0, + childDirectCare: 0, + propertyTax: 0, + wantsToSeeOtherExpenses: false, + earned: 200, + climateControl: false, + TAFDC: 0, + childSupportIn: 0, + otherIncome: 0, + rent: 0, + mortgage: 0, + benefits: Immutable.Set [ + "snap", + ], + otherExpensesEntertainment: 0, + housingInsurance: 0, + otherExpensesClothes: 0, + housing: "homeless", + otherExpensesTransport: 0, + otherExpensesUtilities: 0, + household: Immutable.List [ + Immutable.Map { + m_age: 30, + m_role: "head", + m_disabled: false, }, ], - "housing": "homeless", - "housingInsurance": 0, - "mortgage": 0, - "nonHeatElectricity": false, - "otherExpensesCable": 0, - "otherExpensesCareProducts": 0, - "otherExpensesClothes": 0, - "otherExpensesEntertainment": 0, - "otherExpensesFood": 0, - "otherExpensesMedical": 0, - "otherExpensesOther": 0, - "otherExpensesPhone": 0, - "otherExpensesTransport": 0, - "otherExpensesUtilities": 0, - "otherIncome": 0, - "otherMedical": 0, - "pension": 0, - "phone": false, - "propertyTax": 0, - "rent": 0, - "rentShare": 0, - "socialSecurity": 0, - "unemployment": 0, - "wantsToSeeOtherExpenses": false, - "workersComp": 0, }, - "future": Object { - "SSDI": 0, - "SSI": 0, - "TAFDC": 0, - "adultDirectCare": 0, - "adultOtherCare": 0, - "adultTransportation": 0, - "alimony": 0, - "benefits": Array [], - "childBeforeAndAfterSchoolCare": 0, - "childDirectCare": 0, - "childOtherCare": 0, - "childSupportIn": 0, - "childSupportPaidOut": 0, - "childTransportation": 0, - "climateControl": false, - "contractRent": 0, - "disabledAssistance": 0, - "disabledMedical": 0, - "earned": 100, - "earnedBecauseOfAdultCare": 0, - "earnedBecauseOfChildCare": 0, - "fuelAssistance": false, - "household": Array [ - Object { - "m_age": 30, - "m_disabled": false, - "m_role": "head", + future: Immutable.Map { + otherExpensesCareProducts: 0, + otherMedical: 0, + otherExpensesPhone: 0, + workersComp: 0, + childOtherCare: 0, + earnedBecauseOfAdultCare: 0, + SSDI: 0, + unemployment: 0, + otherExpensesOther: 0, + childBeforeAndAfterSchoolCare: 0, + rentShare: 0, + otherExpensesMedical: 0, + adultTransportation: 0, + adultOtherCare: 0, + earnedBecauseOfChildCare: 0, + childTransportation: 0, + otherExpensesFood: 0, + SSI: 0, + alimony: 0, + contractRent: 0, + socialSecurity: 0, + phone: false, + fuelAssistance: false, + pension: 0, + childSupportPaidOut: 0, + otherExpensesCable: 0, + disabledAssistance: 0, + nonHeatElectricity: false, + adultDirectCare: 0, + disabledMedical: 0, + childDirectCare: 0, + propertyTax: 0, + wantsToSeeOtherExpenses: false, + earned: 100, + climateControl: false, + TAFDC: 0, + childSupportIn: 0, + otherIncome: 0, + rent: 0, + mortgage: 0, + benefits: Immutable.Set [ + ], + otherExpensesEntertainment: 0, + housingInsurance: 0, + otherExpensesClothes: 0, + housing: "homeless", + otherExpensesTransport: 0, + otherExpensesUtilities: 0, + household: Immutable.List [ + Immutable.Map { + m_age: 30, + m_role: "head", + m_disabled: false, }, ], - "housing": "homeless", - "housingInsurance": 0, - "mortgage": 0, - "nonHeatElectricity": false, - "otherExpensesCable": 0, - "otherExpensesCareProducts": 0, - "otherExpensesClothes": 0, - "otherExpensesEntertainment": 0, - "otherExpensesFood": 0, - "otherExpensesMedical": 0, - "otherExpensesOther": 0, - "otherExpensesPhone": 0, - "otherExpensesTransport": 0, - "otherExpensesUtilities": 0, - "otherIncome": 0, - "otherMedical": 0, - "pension": 0, - "phone": false, - "propertyTax": 0, - "rent": 0, - "rentShare": 0, - "socialSecurity": 0, - "unemployment": 0, - "wantsToSeeOtherExpenses": false, - "workersComp": 0, }, } } @@ -2707,119 +2717,122 @@ exports[` renders with snap and current earned less than futu } className="some-class" client={ - Object { - "USState": "MA", - "current": Object { - "SSDI": 0, - "SSI": 0, - "TAFDC": 0, - "adultDirectCare": 0, - "adultOtherCare": 0, - "adultTransportation": 0, - "alimony": 0, - "benefits": Array [], - "childBeforeAndAfterSchoolCare": 0, - "childDirectCare": 0, - "childOtherCare": 0, - "childSupportIn": 0, - "childSupportPaidOut": 0, - "childTransportation": 0, - "climateControl": false, - "contractRent": 0, - "disabledAssistance": 0, - "disabledMedical": 0, - "earned": 100, - "earnedBecauseOfAdultCare": 0, - "earnedBecauseOfChildCare": 0, - "fuelAssistance": false, - "household": Array [ - Object { - "m_age": 30, - "m_disabled": false, - "m_role": "head", + Immutable.Map { + USState: "MA", + current: Immutable.Map { + otherExpensesCareProducts: 0, + otherMedical: 0, + otherExpensesPhone: 0, + workersComp: 0, + childOtherCare: 0, + earnedBecauseOfAdultCare: 0, + SSDI: 0, + unemployment: 0, + otherExpensesOther: 0, + childBeforeAndAfterSchoolCare: 0, + rentShare: 0, + otherExpensesMedical: 0, + adultTransportation: 0, + adultOtherCare: 0, + earnedBecauseOfChildCare: 0, + childTransportation: 0, + otherExpensesFood: 0, + SSI: 0, + alimony: 0, + contractRent: 0, + socialSecurity: 0, + phone: false, + fuelAssistance: false, + pension: 0, + childSupportPaidOut: 0, + otherExpensesCable: 0, + disabledAssistance: 0, + nonHeatElectricity: false, + adultDirectCare: 0, + disabledMedical: 0, + childDirectCare: 0, + propertyTax: 0, + wantsToSeeOtherExpenses: false, + earned: 100, + climateControl: false, + TAFDC: 0, + childSupportIn: 0, + otherIncome: 0, + rent: 0, + mortgage: 0, + benefits: Immutable.Set [ + "snap", + ], + otherExpensesEntertainment: 0, + housingInsurance: 0, + otherExpensesClothes: 0, + housing: "homeless", + otherExpensesTransport: 0, + otherExpensesUtilities: 0, + household: Immutable.List [ + Immutable.Map { + m_age: 30, + m_role: "head", + m_disabled: false, }, ], - "housing": "homeless", - "housingInsurance": 0, - "mortgage": 0, - "nonHeatElectricity": false, - "otherExpensesCable": 0, - "otherExpensesCareProducts": 0, - "otherExpensesClothes": 0, - "otherExpensesEntertainment": 0, - "otherExpensesFood": 0, - "otherExpensesMedical": 0, - "otherExpensesOther": 0, - "otherExpensesPhone": 0, - "otherExpensesTransport": 0, - "otherExpensesUtilities": 0, - "otherIncome": 0, - "otherMedical": 0, - "pension": 0, - "phone": false, - "propertyTax": 0, - "rent": 0, - "rentShare": 0, - "socialSecurity": 0, - "unemployment": 0, - "wantsToSeeOtherExpenses": false, - "workersComp": 0, }, - "future": Object { - "SSDI": 0, - "SSI": 0, - "TAFDC": 0, - "adultDirectCare": 0, - "adultOtherCare": 0, - "adultTransportation": 0, - "alimony": 0, - "benefits": Array [], - "childBeforeAndAfterSchoolCare": 0, - "childDirectCare": 0, - "childOtherCare": 0, - "childSupportIn": 0, - "childSupportPaidOut": 0, - "childTransportation": 0, - "climateControl": false, - "contractRent": 0, - "disabledAssistance": 0, - "disabledMedical": 0, - "earned": 200, - "earnedBecauseOfAdultCare": 0, - "earnedBecauseOfChildCare": 0, - "fuelAssistance": false, - "household": Array [ - Object { - "m_age": 30, - "m_disabled": false, - "m_role": "head", + future: Immutable.Map { + otherExpensesCareProducts: 0, + otherMedical: 0, + otherExpensesPhone: 0, + workersComp: 0, + childOtherCare: 0, + earnedBecauseOfAdultCare: 0, + SSDI: 0, + unemployment: 0, + otherExpensesOther: 0, + childBeforeAndAfterSchoolCare: 0, + rentShare: 0, + otherExpensesMedical: 0, + adultTransportation: 0, + adultOtherCare: 0, + earnedBecauseOfChildCare: 0, + childTransportation: 0, + otherExpensesFood: 0, + SSI: 0, + alimony: 0, + contractRent: 0, + socialSecurity: 0, + phone: false, + fuelAssistance: false, + pension: 0, + childSupportPaidOut: 0, + otherExpensesCable: 0, + disabledAssistance: 0, + nonHeatElectricity: false, + adultDirectCare: 0, + disabledMedical: 0, + childDirectCare: 0, + propertyTax: 0, + wantsToSeeOtherExpenses: false, + earned: 200, + climateControl: false, + TAFDC: 0, + childSupportIn: 0, + otherIncome: 0, + rent: 0, + mortgage: 0, + benefits: Immutable.Set [ + ], + otherExpensesEntertainment: 0, + housingInsurance: 0, + otherExpensesClothes: 0, + housing: "homeless", + otherExpensesTransport: 0, + otherExpensesUtilities: 0, + household: Immutable.List [ + Immutable.Map { + m_age: 30, + m_role: "head", + m_disabled: false, }, ], - "housing": "homeless", - "housingInsurance": 0, - "mortgage": 0, - "nonHeatElectricity": false, - "otherExpensesCable": 0, - "otherExpensesCareProducts": 0, - "otherExpensesClothes": 0, - "otherExpensesEntertainment": 0, - "otherExpensesFood": 0, - "otherExpensesMedical": 0, - "otherExpensesOther": 0, - "otherExpensesPhone": 0, - "otherExpensesTransport": 0, - "otherExpensesUtilities": 0, - "otherIncome": 0, - "otherMedical": 0, - "pension": 0, - "phone": false, - "propertyTax": 0, - "rent": 0, - "rentShare": 0, - "socialSecurity": 0, - "unemployment": 0, - "wantsToSeeOtherExpenses": false, - "workersComp": 0, }, } } diff --git a/src/test/forms/output/__snapshots__/StackedAreaGraph.test.js.snap b/src/test/forms/output/__snapshots__/StackedAreaGraph.test.js.snap index 66090f74..24e579e2 100644 --- a/src/test/forms/output/__snapshots__/StackedAreaGraph.test.js.snap +++ b/src/test/forms/output/__snapshots__/StackedAreaGraph.test.js.snap @@ -9,119 +9,121 @@ exports[` renders with both snap and section8 1`] = ` ] } client={ - Object { - "USState": "MA", - "current": Object { - "SSDI": 0, - "SSI": 0, - "TAFDC": 0, - "adultDirectCare": 0, - "adultOtherCare": 0, - "adultTransportation": 0, - "alimony": 0, - "benefits": Array [], - "childBeforeAndAfterSchoolCare": 0, - "childDirectCare": 0, - "childOtherCare": 0, - "childSupportIn": 0, - "childSupportPaidOut": 0, - "childTransportation": 0, - "climateControl": false, - "contractRent": 0, - "disabledAssistance": 0, - "disabledMedical": 0, - "earned": 100, - "earnedBecauseOfAdultCare": 0, - "earnedBecauseOfChildCare": 0, - "fuelAssistance": false, - "household": Array [ - Object { - "m_age": 30, - "m_disabled": false, - "m_role": "head", + Immutable.Map { + USState: "MA", + current: Immutable.Map { + otherExpensesCareProducts: 0, + otherMedical: 0, + otherExpensesPhone: 0, + workersComp: 0, + childOtherCare: 0, + earnedBecauseOfAdultCare: 0, + SSDI: 0, + unemployment: 0, + otherExpensesOther: 0, + childBeforeAndAfterSchoolCare: 0, + rentShare: 0, + otherExpensesMedical: 0, + adultTransportation: 0, + adultOtherCare: 0, + earnedBecauseOfChildCare: 0, + childTransportation: 0, + otherExpensesFood: 0, + SSI: 0, + alimony: 0, + contractRent: 0, + socialSecurity: 0, + phone: false, + fuelAssistance: false, + pension: 0, + childSupportPaidOut: 0, + otherExpensesCable: 0, + disabledAssistance: 0, + nonHeatElectricity: false, + adultDirectCare: 0, + disabledMedical: 0, + childDirectCare: 0, + propertyTax: 0, + wantsToSeeOtherExpenses: false, + earned: 100, + climateControl: false, + TAFDC: 0, + childSupportIn: 0, + otherIncome: 0, + rent: 0, + mortgage: 0, + benefits: Immutable.Set [ + ], + otherExpensesEntertainment: 0, + housingInsurance: 0, + otherExpensesClothes: 0, + housing: "homeless", + otherExpensesTransport: 0, + otherExpensesUtilities: 0, + household: Immutable.List [ + Immutable.Map { + m_age: 30, + m_role: "head", + m_disabled: false, }, ], - "housing": "homeless", - "housingInsurance": 0, - "mortgage": 0, - "nonHeatElectricity": false, - "otherExpensesCable": 0, - "otherExpensesCareProducts": 0, - "otherExpensesClothes": 0, - "otherExpensesEntertainment": 0, - "otherExpensesFood": 0, - "otherExpensesMedical": 0, - "otherExpensesOther": 0, - "otherExpensesPhone": 0, - "otherExpensesTransport": 0, - "otherExpensesUtilities": 0, - "otherIncome": 0, - "otherMedical": 0, - "pension": 0, - "phone": false, - "propertyTax": 0, - "rent": 0, - "rentShare": 0, - "socialSecurity": 0, - "unemployment": 0, - "wantsToSeeOtherExpenses": false, - "workersComp": 0, }, - "future": Object { - "SSDI": 0, - "SSI": 0, - "TAFDC": 0, - "adultDirectCare": 0, - "adultOtherCare": 0, - "adultTransportation": 0, - "alimony": 0, - "benefits": Array [], - "childBeforeAndAfterSchoolCare": 0, - "childDirectCare": 0, - "childOtherCare": 0, - "childSupportIn": 0, - "childSupportPaidOut": 0, - "childTransportation": 0, - "climateControl": false, - "contractRent": 0, - "disabledAssistance": 0, - "disabledMedical": 0, - "earned": 200, - "earnedBecauseOfAdultCare": 0, - "earnedBecauseOfChildCare": 0, - "fuelAssistance": false, - "household": Array [ - Object { - "m_age": 30, - "m_disabled": false, - "m_role": "head", + future: Immutable.Map { + otherExpensesCareProducts: 0, + otherMedical: 0, + otherExpensesPhone: 0, + workersComp: 0, + childOtherCare: 0, + earnedBecauseOfAdultCare: 0, + SSDI: 0, + unemployment: 0, + otherExpensesOther: 0, + childBeforeAndAfterSchoolCare: 0, + rentShare: 0, + otherExpensesMedical: 0, + adultTransportation: 0, + adultOtherCare: 0, + earnedBecauseOfChildCare: 0, + childTransportation: 0, + otherExpensesFood: 0, + SSI: 0, + alimony: 0, + contractRent: 0, + socialSecurity: 0, + phone: false, + fuelAssistance: false, + pension: 0, + childSupportPaidOut: 0, + otherExpensesCable: 0, + disabledAssistance: 0, + nonHeatElectricity: false, + adultDirectCare: 0, + disabledMedical: 0, + childDirectCare: 0, + propertyTax: 0, + wantsToSeeOtherExpenses: false, + earned: 200, + climateControl: false, + TAFDC: 0, + childSupportIn: 0, + otherIncome: 0, + rent: 0, + mortgage: 0, + benefits: Immutable.Set [ + ], + otherExpensesEntertainment: 0, + housingInsurance: 0, + otherExpensesClothes: 0, + housing: "homeless", + otherExpensesTransport: 0, + otherExpensesUtilities: 0, + household: Immutable.List [ + Immutable.Map { + m_age: 30, + m_role: "head", + m_disabled: false, }, ], - "housing": "homeless", - "housingInsurance": 0, - "mortgage": 0, - "nonHeatElectricity": false, - "otherExpensesCable": 0, - "otherExpensesCareProducts": 0, - "otherExpensesClothes": 0, - "otherExpensesEntertainment": 0, - "otherExpensesFood": 0, - "otherExpensesMedical": 0, - "otherExpensesOther": 0, - "otherExpensesPhone": 0, - "otherExpensesTransport": 0, - "otherExpensesUtilities": 0, - "otherIncome": 0, - "otherMedical": 0, - "pension": 0, - "phone": false, - "propertyTax": 0, - "rent": 0, - "rentShare": 0, - "socialSecurity": 0, - "unemployment": 0, - "wantsToSeeOtherExpenses": false, - "workersComp": 0, }, } } @@ -1277,119 +1279,121 @@ exports[` renders with section8 1`] = ` ] } client={ - Object { - "USState": "MA", - "current": Object { - "SSDI": 0, - "SSI": 0, - "TAFDC": 0, - "adultDirectCare": 0, - "adultOtherCare": 0, - "adultTransportation": 0, - "alimony": 0, - "benefits": Array [], - "childBeforeAndAfterSchoolCare": 0, - "childDirectCare": 0, - "childOtherCare": 0, - "childSupportIn": 0, - "childSupportPaidOut": 0, - "childTransportation": 0, - "climateControl": false, - "contractRent": 0, - "disabledAssistance": 0, - "disabledMedical": 0, - "earned": 100, - "earnedBecauseOfAdultCare": 0, - "earnedBecauseOfChildCare": 0, - "fuelAssistance": false, - "household": Array [ - Object { - "m_age": 30, - "m_disabled": false, - "m_role": "head", + Immutable.Map { + USState: "MA", + current: Immutable.Map { + otherExpensesCareProducts: 0, + otherMedical: 0, + otherExpensesPhone: 0, + workersComp: 0, + childOtherCare: 0, + earnedBecauseOfAdultCare: 0, + SSDI: 0, + unemployment: 0, + otherExpensesOther: 0, + childBeforeAndAfterSchoolCare: 0, + rentShare: 0, + otherExpensesMedical: 0, + adultTransportation: 0, + adultOtherCare: 0, + earnedBecauseOfChildCare: 0, + childTransportation: 0, + otherExpensesFood: 0, + SSI: 0, + alimony: 0, + contractRent: 0, + socialSecurity: 0, + phone: false, + fuelAssistance: false, + pension: 0, + childSupportPaidOut: 0, + otherExpensesCable: 0, + disabledAssistance: 0, + nonHeatElectricity: false, + adultDirectCare: 0, + disabledMedical: 0, + childDirectCare: 0, + propertyTax: 0, + wantsToSeeOtherExpenses: false, + earned: 100, + climateControl: false, + TAFDC: 0, + childSupportIn: 0, + otherIncome: 0, + rent: 0, + mortgage: 0, + benefits: Immutable.Set [ + ], + otherExpensesEntertainment: 0, + housingInsurance: 0, + otherExpensesClothes: 0, + housing: "homeless", + otherExpensesTransport: 0, + otherExpensesUtilities: 0, + household: Immutable.List [ + Immutable.Map { + m_age: 30, + m_role: "head", + m_disabled: false, }, ], - "housing": "homeless", - "housingInsurance": 0, - "mortgage": 0, - "nonHeatElectricity": false, - "otherExpensesCable": 0, - "otherExpensesCareProducts": 0, - "otherExpensesClothes": 0, - "otherExpensesEntertainment": 0, - "otherExpensesFood": 0, - "otherExpensesMedical": 0, - "otherExpensesOther": 0, - "otherExpensesPhone": 0, - "otherExpensesTransport": 0, - "otherExpensesUtilities": 0, - "otherIncome": 0, - "otherMedical": 0, - "pension": 0, - "phone": false, - "propertyTax": 0, - "rent": 0, - "rentShare": 0, - "socialSecurity": 0, - "unemployment": 0, - "wantsToSeeOtherExpenses": false, - "workersComp": 0, }, - "future": Object { - "SSDI": 0, - "SSI": 0, - "TAFDC": 0, - "adultDirectCare": 0, - "adultOtherCare": 0, - "adultTransportation": 0, - "alimony": 0, - "benefits": Array [], - "childBeforeAndAfterSchoolCare": 0, - "childDirectCare": 0, - "childOtherCare": 0, - "childSupportIn": 0, - "childSupportPaidOut": 0, - "childTransportation": 0, - "climateControl": false, - "contractRent": 0, - "disabledAssistance": 0, - "disabledMedical": 0, - "earned": 200, - "earnedBecauseOfAdultCare": 0, - "earnedBecauseOfChildCare": 0, - "fuelAssistance": false, - "household": Array [ - Object { - "m_age": 30, - "m_disabled": false, - "m_role": "head", + future: Immutable.Map { + otherExpensesCareProducts: 0, + otherMedical: 0, + otherExpensesPhone: 0, + workersComp: 0, + childOtherCare: 0, + earnedBecauseOfAdultCare: 0, + SSDI: 0, + unemployment: 0, + otherExpensesOther: 0, + childBeforeAndAfterSchoolCare: 0, + rentShare: 0, + otherExpensesMedical: 0, + adultTransportation: 0, + adultOtherCare: 0, + earnedBecauseOfChildCare: 0, + childTransportation: 0, + otherExpensesFood: 0, + SSI: 0, + alimony: 0, + contractRent: 0, + socialSecurity: 0, + phone: false, + fuelAssistance: false, + pension: 0, + childSupportPaidOut: 0, + otherExpensesCable: 0, + disabledAssistance: 0, + nonHeatElectricity: false, + adultDirectCare: 0, + disabledMedical: 0, + childDirectCare: 0, + propertyTax: 0, + wantsToSeeOtherExpenses: false, + earned: 200, + climateControl: false, + TAFDC: 0, + childSupportIn: 0, + otherIncome: 0, + rent: 0, + mortgage: 0, + benefits: Immutable.Set [ + ], + otherExpensesEntertainment: 0, + housingInsurance: 0, + otherExpensesClothes: 0, + housing: "homeless", + otherExpensesTransport: 0, + otherExpensesUtilities: 0, + household: Immutable.List [ + Immutable.Map { + m_age: 30, + m_role: "head", + m_disabled: false, }, ], - "housing": "homeless", - "housingInsurance": 0, - "mortgage": 0, - "nonHeatElectricity": false, - "otherExpensesCable": 0, - "otherExpensesCareProducts": 0, - "otherExpensesClothes": 0, - "otherExpensesEntertainment": 0, - "otherExpensesFood": 0, - "otherExpensesMedical": 0, - "otherExpensesOther": 0, - "otherExpensesPhone": 0, - "otherExpensesTransport": 0, - "otherExpensesUtilities": 0, - "otherIncome": 0, - "otherMedical": 0, - "pension": 0, - "phone": false, - "propertyTax": 0, - "rent": 0, - "rentShare": 0, - "socialSecurity": 0, - "unemployment": 0, - "wantsToSeeOtherExpenses": false, - "workersComp": 0, }, } } @@ -2341,119 +2345,121 @@ exports[` renders with snap and current earned greater than fu ] } client={ - Object { - "USState": "MA", - "current": Object { - "SSDI": 0, - "SSI": 0, - "TAFDC": 0, - "adultDirectCare": 0, - "adultOtherCare": 0, - "adultTransportation": 0, - "alimony": 0, - "benefits": Array [], - "childBeforeAndAfterSchoolCare": 0, - "childDirectCare": 0, - "childOtherCare": 0, - "childSupportIn": 0, - "childSupportPaidOut": 0, - "childTransportation": 0, - "climateControl": false, - "contractRent": 0, - "disabledAssistance": 0, - "disabledMedical": 0, - "earned": 200, - "earnedBecauseOfAdultCare": 0, - "earnedBecauseOfChildCare": 0, - "fuelAssistance": false, - "household": Array [ - Object { - "m_age": 30, - "m_disabled": false, - "m_role": "head", + Immutable.Map { + USState: "MA", + current: Immutable.Map { + otherExpensesCareProducts: 0, + otherMedical: 0, + otherExpensesPhone: 0, + workersComp: 0, + childOtherCare: 0, + earnedBecauseOfAdultCare: 0, + SSDI: 0, + unemployment: 0, + otherExpensesOther: 0, + childBeforeAndAfterSchoolCare: 0, + rentShare: 0, + otherExpensesMedical: 0, + adultTransportation: 0, + adultOtherCare: 0, + earnedBecauseOfChildCare: 0, + childTransportation: 0, + otherExpensesFood: 0, + SSI: 0, + alimony: 0, + contractRent: 0, + socialSecurity: 0, + phone: false, + fuelAssistance: false, + pension: 0, + childSupportPaidOut: 0, + otherExpensesCable: 0, + disabledAssistance: 0, + nonHeatElectricity: false, + adultDirectCare: 0, + disabledMedical: 0, + childDirectCare: 0, + propertyTax: 0, + wantsToSeeOtherExpenses: false, + earned: 200, + climateControl: false, + TAFDC: 0, + childSupportIn: 0, + otherIncome: 0, + rent: 0, + mortgage: 0, + benefits: Immutable.Set [ + ], + otherExpensesEntertainment: 0, + housingInsurance: 0, + otherExpensesClothes: 0, + housing: "homeless", + otherExpensesTransport: 0, + otherExpensesUtilities: 0, + household: Immutable.List [ + Immutable.Map { + m_age: 30, + m_role: "head", + m_disabled: false, }, ], - "housing": "homeless", - "housingInsurance": 0, - "mortgage": 0, - "nonHeatElectricity": false, - "otherExpensesCable": 0, - "otherExpensesCareProducts": 0, - "otherExpensesClothes": 0, - "otherExpensesEntertainment": 0, - "otherExpensesFood": 0, - "otherExpensesMedical": 0, - "otherExpensesOther": 0, - "otherExpensesPhone": 0, - "otherExpensesTransport": 0, - "otherExpensesUtilities": 0, - "otherIncome": 0, - "otherMedical": 0, - "pension": 0, - "phone": false, - "propertyTax": 0, - "rent": 0, - "rentShare": 0, - "socialSecurity": 0, - "unemployment": 0, - "wantsToSeeOtherExpenses": false, - "workersComp": 0, }, - "future": Object { - "SSDI": 0, - "SSI": 0, - "TAFDC": 0, - "adultDirectCare": 0, - "adultOtherCare": 0, - "adultTransportation": 0, - "alimony": 0, - "benefits": Array [], - "childBeforeAndAfterSchoolCare": 0, - "childDirectCare": 0, - "childOtherCare": 0, - "childSupportIn": 0, - "childSupportPaidOut": 0, - "childTransportation": 0, - "climateControl": false, - "contractRent": 0, - "disabledAssistance": 0, - "disabledMedical": 0, - "earned": 100, - "earnedBecauseOfAdultCare": 0, - "earnedBecauseOfChildCare": 0, - "fuelAssistance": false, - "household": Array [ - Object { - "m_age": 30, - "m_disabled": false, - "m_role": "head", + future: Immutable.Map { + otherExpensesCareProducts: 0, + otherMedical: 0, + otherExpensesPhone: 0, + workersComp: 0, + childOtherCare: 0, + earnedBecauseOfAdultCare: 0, + SSDI: 0, + unemployment: 0, + otherExpensesOther: 0, + childBeforeAndAfterSchoolCare: 0, + rentShare: 0, + otherExpensesMedical: 0, + adultTransportation: 0, + adultOtherCare: 0, + earnedBecauseOfChildCare: 0, + childTransportation: 0, + otherExpensesFood: 0, + SSI: 0, + alimony: 0, + contractRent: 0, + socialSecurity: 0, + phone: false, + fuelAssistance: false, + pension: 0, + childSupportPaidOut: 0, + otherExpensesCable: 0, + disabledAssistance: 0, + nonHeatElectricity: false, + adultDirectCare: 0, + disabledMedical: 0, + childDirectCare: 0, + propertyTax: 0, + wantsToSeeOtherExpenses: false, + earned: 100, + climateControl: false, + TAFDC: 0, + childSupportIn: 0, + otherIncome: 0, + rent: 0, + mortgage: 0, + benefits: Immutable.Set [ + ], + otherExpensesEntertainment: 0, + housingInsurance: 0, + otherExpensesClothes: 0, + housing: "homeless", + otherExpensesTransport: 0, + otherExpensesUtilities: 0, + household: Immutable.List [ + Immutable.Map { + m_age: 30, + m_role: "head", + m_disabled: false, }, ], - "housing": "homeless", - "housingInsurance": 0, - "mortgage": 0, - "nonHeatElectricity": false, - "otherExpensesCable": 0, - "otherExpensesCareProducts": 0, - "otherExpensesClothes": 0, - "otherExpensesEntertainment": 0, - "otherExpensesFood": 0, - "otherExpensesMedical": 0, - "otherExpensesOther": 0, - "otherExpensesPhone": 0, - "otherExpensesTransport": 0, - "otherExpensesUtilities": 0, - "otherIncome": 0, - "otherMedical": 0, - "pension": 0, - "phone": false, - "propertyTax": 0, - "rent": 0, - "rentShare": 0, - "socialSecurity": 0, - "unemployment": 0, - "wantsToSeeOtherExpenses": false, - "workersComp": 0, }, } } @@ -3405,119 +3411,121 @@ exports[` renders with snap and current earned less than futur ] } client={ - Object { - "USState": "MA", - "current": Object { - "SSDI": 0, - "SSI": 0, - "TAFDC": 0, - "adultDirectCare": 0, - "adultOtherCare": 0, - "adultTransportation": 0, - "alimony": 0, - "benefits": Array [], - "childBeforeAndAfterSchoolCare": 0, - "childDirectCare": 0, - "childOtherCare": 0, - "childSupportIn": 0, - "childSupportPaidOut": 0, - "childTransportation": 0, - "climateControl": false, - "contractRent": 0, - "disabledAssistance": 0, - "disabledMedical": 0, - "earned": 100, - "earnedBecauseOfAdultCare": 0, - "earnedBecauseOfChildCare": 0, - "fuelAssistance": false, - "household": Array [ - Object { - "m_age": 30, - "m_disabled": false, - "m_role": "head", + Immutable.Map { + USState: "MA", + current: Immutable.Map { + otherExpensesCareProducts: 0, + otherMedical: 0, + otherExpensesPhone: 0, + workersComp: 0, + childOtherCare: 0, + earnedBecauseOfAdultCare: 0, + SSDI: 0, + unemployment: 0, + otherExpensesOther: 0, + childBeforeAndAfterSchoolCare: 0, + rentShare: 0, + otherExpensesMedical: 0, + adultTransportation: 0, + adultOtherCare: 0, + earnedBecauseOfChildCare: 0, + childTransportation: 0, + otherExpensesFood: 0, + SSI: 0, + alimony: 0, + contractRent: 0, + socialSecurity: 0, + phone: false, + fuelAssistance: false, + pension: 0, + childSupportPaidOut: 0, + otherExpensesCable: 0, + disabledAssistance: 0, + nonHeatElectricity: false, + adultDirectCare: 0, + disabledMedical: 0, + childDirectCare: 0, + propertyTax: 0, + wantsToSeeOtherExpenses: false, + earned: 100, + climateControl: false, + TAFDC: 0, + childSupportIn: 0, + otherIncome: 0, + rent: 0, + mortgage: 0, + benefits: Immutable.Set [ + ], + otherExpensesEntertainment: 0, + housingInsurance: 0, + otherExpensesClothes: 0, + housing: "homeless", + otherExpensesTransport: 0, + otherExpensesUtilities: 0, + household: Immutable.List [ + Immutable.Map { + m_age: 30, + m_role: "head", + m_disabled: false, }, ], - "housing": "homeless", - "housingInsurance": 0, - "mortgage": 0, - "nonHeatElectricity": false, - "otherExpensesCable": 0, - "otherExpensesCareProducts": 0, - "otherExpensesClothes": 0, - "otherExpensesEntertainment": 0, - "otherExpensesFood": 0, - "otherExpensesMedical": 0, - "otherExpensesOther": 0, - "otherExpensesPhone": 0, - "otherExpensesTransport": 0, - "otherExpensesUtilities": 0, - "otherIncome": 0, - "otherMedical": 0, - "pension": 0, - "phone": false, - "propertyTax": 0, - "rent": 0, - "rentShare": 0, - "socialSecurity": 0, - "unemployment": 0, - "wantsToSeeOtherExpenses": false, - "workersComp": 0, }, - "future": Object { - "SSDI": 0, - "SSI": 0, - "TAFDC": 0, - "adultDirectCare": 0, - "adultOtherCare": 0, - "adultTransportation": 0, - "alimony": 0, - "benefits": Array [], - "childBeforeAndAfterSchoolCare": 0, - "childDirectCare": 0, - "childOtherCare": 0, - "childSupportIn": 0, - "childSupportPaidOut": 0, - "childTransportation": 0, - "climateControl": false, - "contractRent": 0, - "disabledAssistance": 0, - "disabledMedical": 0, - "earned": 200, - "earnedBecauseOfAdultCare": 0, - "earnedBecauseOfChildCare": 0, - "fuelAssistance": false, - "household": Array [ - Object { - "m_age": 30, - "m_disabled": false, - "m_role": "head", + future: Immutable.Map { + otherExpensesCareProducts: 0, + otherMedical: 0, + otherExpensesPhone: 0, + workersComp: 0, + childOtherCare: 0, + earnedBecauseOfAdultCare: 0, + SSDI: 0, + unemployment: 0, + otherExpensesOther: 0, + childBeforeAndAfterSchoolCare: 0, + rentShare: 0, + otherExpensesMedical: 0, + adultTransportation: 0, + adultOtherCare: 0, + earnedBecauseOfChildCare: 0, + childTransportation: 0, + otherExpensesFood: 0, + SSI: 0, + alimony: 0, + contractRent: 0, + socialSecurity: 0, + phone: false, + fuelAssistance: false, + pension: 0, + childSupportPaidOut: 0, + otherExpensesCable: 0, + disabledAssistance: 0, + nonHeatElectricity: false, + adultDirectCare: 0, + disabledMedical: 0, + childDirectCare: 0, + propertyTax: 0, + wantsToSeeOtherExpenses: false, + earned: 200, + climateControl: false, + TAFDC: 0, + childSupportIn: 0, + otherIncome: 0, + rent: 0, + mortgage: 0, + benefits: Immutable.Set [ + ], + otherExpensesEntertainment: 0, + housingInsurance: 0, + otherExpensesClothes: 0, + housing: "homeless", + otherExpensesTransport: 0, + otherExpensesUtilities: 0, + household: Immutable.List [ + Immutable.Map { + m_age: 30, + m_role: "head", + m_disabled: false, }, ], - "housing": "homeless", - "housingInsurance": 0, - "mortgage": 0, - "nonHeatElectricity": false, - "otherExpensesCable": 0, - "otherExpensesCareProducts": 0, - "otherExpensesClothes": 0, - "otherExpensesEntertainment": 0, - "otherExpensesFood": 0, - "otherExpensesMedical": 0, - "otherExpensesOther": 0, - "otherExpensesPhone": 0, - "otherExpensesTransport": 0, - "otherExpensesUtilities": 0, - "otherIncome": 0, - "otherMedical": 0, - "pension": 0, - "phone": false, - "propertyTax": 0, - "rent": 0, - "rentShare": 0, - "socialSecurity": 0, - "unemployment": 0, - "wantsToSeeOtherExpenses": false, - "workersComp": 0, }, } } diff --git a/src/test/forms/output/__snapshots__/StackedBarGraph.test.js.snap b/src/test/forms/output/__snapshots__/StackedBarGraph.test.js.snap index 29f5f53f..b681b906 100644 --- a/src/test/forms/output/__snapshots__/StackedBarGraph.test.js.snap +++ b/src/test/forms/output/__snapshots__/StackedBarGraph.test.js.snap @@ -3,122 +3,123 @@ exports[` renders with both snap and section8 1`] = ` renders with both snap and section8 1`] = ` exports[` renders with section8 1`] = ` renders with section8 1`] = ` exports[` renders with snap and current earned greater than future earned 1`] = ` renders with snap and current earned greater than fut exports[` renders with snap and current earned less than future earned 1`] = ` { +import createReducer from '../../../reducers'; +import { setCashValue } from '../../../actions'; + +const reducer = createReducer(); + +const buildField = (state) => { const props = { - timeState: defaultsDeep({}, values, CLIENT_DEFAULTS.current), - updateClientValue: jest.fn(), + timeState: state.getIn([ + 'client', + 'current', + ]), + updateClientValue: function() {}, }; return mount(); }; describe('ContractRentField.storeValidator()', () => { it('when input valid, sets state to valid', () => { - const field = buildField({ rentShare: 0 }); + const state = reducer( + undefined, + setCashValue({ + time: 'current', + name: 'rentShare', + value: 0, + }) + ); + + const field = buildField(state); expect(field.state()).toEqual({ valid: true, message: null }); field.instance().storeValidator(1); @@ -24,7 +39,12 @@ describe('ContractRentField.storeValidator()', () => { }); it('when input negative, sets state to invalid', () => { - const field = buildField(); + const state = reducer( + undefined, + {} + ); + + const field = buildField(state); expect(field.state()).toEqual({ valid: true, message: null }); field.instance().storeValidator(-1); @@ -33,7 +53,16 @@ describe('ContractRentField.storeValidator()', () => { }); it('when contract rent less than rent share, sets state to invalid', () => { - const field = buildField({ rentShare: 2 }); + const state = reducer( + undefined, + setCashValue({ + time: 'current', + name: 'rentShare', + value: 2, + }) + ); + + const field = buildField(state); expect(field.state()).toEqual({ valid: true, message: null }); field.instance().storeValidator(1); @@ -44,7 +73,12 @@ describe('ContractRentField.storeValidator()', () => { describe('ContractRentField.onBlur', () => { it('resets state', () => { - const field = buildField(); + const state = reducer( + undefined, + {} + ); + + const field = buildField(state); field.setState({ valid: false, message: 'Not the default value' }); field.instance().onBlur(); diff --git a/src/test/forms/rentFields/PlainRentRow.test.js b/src/test/forms/rentFields/PlainRentRow.test.js index 3ff07736..31352ed8 100644 --- a/src/test/forms/rentFields/PlainRentRow.test.js +++ b/src/test/forms/rentFields/PlainRentRow.test.js @@ -1,15 +1,22 @@ import React from 'react'; import { mount } from 'enzyme'; -import { cloneDeep } from 'lodash'; -import { CLIENT_DEFAULTS } from '../../../utils/CLIENT_DEFAULTS'; import { PlainRentRow } from '../../../forms/rentFields'; +import createReducer from '../../../reducers'; + +const reducer = createReducer(); + describe('', () => { it('renders', () => { + const state = reducer(undefined, {}); + const props = { - timeState: cloneDeep(CLIENT_DEFAULTS.current), - updateClientValue: jest.fn(), + timeState: state.getIn([ + 'client', + 'current', + ]), + setValue: function() {}, }; expect(() => { mount(); diff --git a/src/test/forms/rentFields/RentShareField.test.js b/src/test/forms/rentFields/RentShareField.test.js index 50265230..4a482d32 100644 --- a/src/test/forms/rentFields/RentShareField.test.js +++ b/src/test/forms/rentFields/RentShareField.test.js @@ -1,21 +1,37 @@ import React from 'react'; import { mount } from 'enzyme'; -import { defaultsDeep } from 'lodash'; -import { CLIENT_DEFAULTS } from '../../../utils/CLIENT_DEFAULTS'; import { RentShareField } from '../../../forms/rentFields'; -const buildField = (values = {}) => { +import createReducer from '../../../reducers'; +import { setCashValue } from '../../../actions'; + +const reducer = createReducer(); + +const buildField = (state) => { const props = { - timeState: defaultsDeep({}, values, CLIENT_DEFAULTS.current), - updateClientValue: jest.fn(), + timeState: state.getIn([ + 'client', + 'current', + ]), + updateClientValue: function() {}, }; + return mount(); }; describe('RentShareField.storeValidator()', () => { it('when input valid, sets state to valid', () => { - const field = buildField({ contractRent: 2 }); + const state = reducer( + undefined, + setCashValue({ + time: 'current', + name: 'contractRent', + value: 2, + }) + ); + + const field = buildField(state); expect(field.state()).toEqual({ valid: true, message: null }); field.instance().storeValidator(1); @@ -24,7 +40,12 @@ describe('RentShareField.storeValidator()', () => { }); it('when input negative, sets state to invalid', () => { - const field = buildField(); + const state = reducer( + undefined, + {} + ); + + const field = buildField(state); expect(field.state()).toEqual({ valid: true, message: null }); field.instance().storeValidator(-1); @@ -33,7 +54,16 @@ describe('RentShareField.storeValidator()', () => { }); it('when rent share greater than contract rent, sets state to invalid', () => { - const field = buildField({ contractRent: 1 }); + const state = reducer( + undefined, + setCashValue({ + time: 'current', + name: 'contractRent', + value: 1, + }) + ); + + const field = buildField(state); expect(field.state()).toEqual({ valid: true, message: null }); field.instance().storeValidator(2); @@ -44,7 +74,12 @@ describe('RentShareField.storeValidator()', () => { describe('RentShareField.onBlur', () => { it('resets state', () => { - const field = buildField(); + const state = reducer( + undefined, + {} + ); + + const field = buildField(state); field.setState({ valid: false, message: 'Not the default value' }); field.instance().onBlur();