From a27c6f532535353f0cc4359dcb958a27971c301c Mon Sep 17 00:00:00 2001 From: Chris Kim <42885441+chriskim2311@users.noreply.github.com> Date: Thu, 11 Apr 2024 11:40:02 -0700 Subject: [PATCH] VACMS-16913: DUW questions 3 (#29030) --- .../discharge-wizard/actions/index.js | 38 +++++++- .../components/DischargeWizardApp.jsx | 3 +- .../components/v2/BreadcrumbsV2.jsx | 22 +++++ .../components/v2/Homepage.jsx | 1 + .../components/v2/questions/CourtMartial.jsx | 79 +++++++++++++++ .../v2/questions/DischargeMonth.jsx | 6 +- .../components/v2/questions/DischargeType.jsx | 79 +++++++++++++++ .../components/v2/questions/DischargeYear.jsx | 4 +- .../components/v2/questions/Intention.jsx | 79 +++++++++++++++ .../components/v2/questions/Reason.jsx | 95 +++++++++++++++++++ .../v2/questions/shared/Dropdown.jsx | 2 +- .../v2/questions/shared/RadioGroup.jsx | 7 +- .../constants/display-conditions.js | 73 ++++++++++++++ .../discharge-wizard/constants/index.js | 30 +++++- .../constants/question-data-map.js | 38 +++++++- .../reducers/v2/discharge-upgrade-wizard.js | 22 ++++- src/applications/discharge-wizard/routes.jsx | 8 ++ .../sass/discharge-wizard.scss | 3 - .../tests/v2/CourtMartial.unit.spec.js | 80 ++++++++++++++++ .../tests/v2/DischargeMonth.unit.spec.js | 25 +---- .../tests/v2/DischargeType.unit.spec.js | 80 ++++++++++++++++ .../tests/v2/DischargeYear.unit.spec.js | 25 +---- .../tests/v2/Intention.unit.spec.js | 80 ++++++++++++++++ .../tests/v2/Reason.unit.spec.js | 80 ++++++++++++++++ .../discharge-upgrade-wizard.cypress.spec.js | 57 +++++++++++ .../tests/v2/cypress/helpers.js | 55 +++++++++++ .../tests/v2/display-logic.unit.spec.js | 4 +- .../utilities/display-logic-questions.js | 59 ++++++++---- .../utilities/page-navigation.js | 61 ++++++++++-- .../discharge-wizard/utilities/page-setup.js | 1 - 30 files changed, 1103 insertions(+), 93 deletions(-) create mode 100644 src/applications/discharge-wizard/components/v2/BreadcrumbsV2.jsx create mode 100644 src/applications/discharge-wizard/components/v2/questions/CourtMartial.jsx create mode 100644 src/applications/discharge-wizard/components/v2/questions/DischargeType.jsx create mode 100644 src/applications/discharge-wizard/components/v2/questions/Intention.jsx create mode 100644 src/applications/discharge-wizard/components/v2/questions/Reason.jsx create mode 100644 src/applications/discharge-wizard/constants/display-conditions.js create mode 100644 src/applications/discharge-wizard/tests/v2/CourtMartial.unit.spec.js create mode 100644 src/applications/discharge-wizard/tests/v2/DischargeType.unit.spec.js create mode 100644 src/applications/discharge-wizard/tests/v2/Intention.unit.spec.js create mode 100644 src/applications/discharge-wizard/tests/v2/Reason.unit.spec.js create mode 100644 src/applications/discharge-wizard/tests/v2/cypress/discharge-upgrade-wizard.cypress.spec.js create mode 100644 src/applications/discharge-wizard/tests/v2/cypress/helpers.js diff --git a/src/applications/discharge-wizard/actions/index.js b/src/applications/discharge-wizard/actions/index.js index 4e32d8121b5c..13034e9bbc0d 100644 --- a/src/applications/discharge-wizard/actions/index.js +++ b/src/applications/discharge-wizard/actions/index.js @@ -1,11 +1,15 @@ import { DW_UPDATE_FIELD, // v2 actions + DUW_UPDATE_FORM_STORE, DUW_VIEWED_INTRO_PAGE, DUW_UPDATE_SERVICE_BRANCH, DUW_UPDATE_DISCHARGE_YEAR, DUW_UPDATE_DISCHARGE_MONTH, - DUW_UPDATE_FORM_STORE, + DUW_UPDATE_REASON, + DUW_UPDATE_DISCHARGE_TYPE, + DUW_UPDATE_COURT_MARTIAL, + DUW_UPDATE_INTENTION, } from '../constants'; export const updateField = (key, value) => { @@ -16,6 +20,13 @@ export const updateField = (key, value) => { }; }; +export const updateFormStore = value => { + return { + type: DUW_UPDATE_FORM_STORE, + payload: value, + }; +}; + export const updateIntroPageViewed = value => { return { type: DUW_VIEWED_INTRO_PAGE, @@ -44,9 +55,30 @@ export const updateDischargeMonth = value => { }; }; -export const updateFormStore = value => { +export const updateReason = value => { return { - type: DUW_UPDATE_FORM_STORE, + type: DUW_UPDATE_REASON, + payload: value, + }; +}; + +export const updateCourtMartial = value => { + return { + type: DUW_UPDATE_COURT_MARTIAL, + payload: value, + }; +}; + +export const updateIntention = value => { + return { + type: DUW_UPDATE_INTENTION, + payload: value, + }; +}; + +export const updateDischargeType = value => { + return { + type: DUW_UPDATE_DISCHARGE_TYPE, payload: value, }; }; diff --git a/src/applications/discharge-wizard/components/DischargeWizardApp.jsx b/src/applications/discharge-wizard/components/DischargeWizardApp.jsx index 093b129b7fc5..48130a940c11 100644 --- a/src/applications/discharge-wizard/components/DischargeWizardApp.jsx +++ b/src/applications/discharge-wizard/components/DischargeWizardApp.jsx @@ -2,6 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import environment from 'platform/utilities/environment'; import Breadcrumbs from './Breadcrumbs'; +import BreadcrumbsV2 from './v2/BreadcrumbsV2'; export default function DischargeWizardApp({ children }) { const isProd = environment.isProduction(); @@ -16,7 +17,7 @@ export default function DischargeWizardApp({ children }) { } return (
- +
{children}
); diff --git a/src/applications/discharge-wizard/components/v2/BreadcrumbsV2.jsx b/src/applications/discharge-wizard/components/v2/BreadcrumbsV2.jsx new file mode 100644 index 000000000000..263be14b8421 --- /dev/null +++ b/src/applications/discharge-wizard/components/v2/BreadcrumbsV2.jsx @@ -0,0 +1,22 @@ +import React from 'react'; +import { VaBreadcrumbs } from '@department-of-veterans-affairs/web-components/react-bindings'; + +const BreadcrumbsV2 = () => ( + +); + +export default BreadcrumbsV2; diff --git a/src/applications/discharge-wizard/components/v2/Homepage.jsx b/src/applications/discharge-wizard/components/v2/Homepage.jsx index f1197ee02b17..ebe2e56d18d0 100644 --- a/src/applications/discharge-wizard/components/v2/Homepage.jsx +++ b/src/applications/discharge-wizard/components/v2/Homepage.jsx @@ -69,6 +69,7 @@ const HomePage = ({ router, setIntroPageViewed }) => { {/* eslint-disable-next-line jsx-a11y/anchor-is-valid */} diff --git a/src/applications/discharge-wizard/components/v2/questions/CourtMartial.jsx b/src/applications/discharge-wizard/components/v2/questions/CourtMartial.jsx new file mode 100644 index 000000000000..33f0d71cc377 --- /dev/null +++ b/src/applications/discharge-wizard/components/v2/questions/CourtMartial.jsx @@ -0,0 +1,79 @@ +import React, { useEffect, useState } from 'react'; +import PropTypes from 'prop-types'; +import { connect } from 'react-redux'; +import { + QUESTION_MAP, + RESPONSES, + SHORT_NAME_MAP, +} from '../../../constants/question-data-map'; +import RadioGroup from './shared/RadioGroup'; +import { updateCourtMartial } from '../../../actions'; +import { pageSetup } from '../../../utilities/page-setup'; +import { ROUTES } from '../../../constants'; + +const CourtMartial = ({ + formResponses, + setCourtMartial, + router, + viewedIntroPage, +}) => { + const [formError, setFormError] = useState(false); + const shortName = SHORT_NAME_MAP.COURT_MARTIAL; + const H1 = QUESTION_MAP[shortName]; + const courtMartial = formResponses[shortName]; + const { COURT_MARTIAL_1, COURT_MARTIAL_2, COURT_MARTIAL_3 } = RESPONSES; + + useEffect( + () => { + pageSetup(H1); + }, + [H1], + ); + + useEffect( + () => { + if (!viewedIntroPage) { + router.push(ROUTES.HOME); + } + }, + [router, viewedIntroPage], + ); + + return ( + + ); +}; + +CourtMartial.propTypes = { + formResponses: PropTypes.object, + router: PropTypes.shape({ + push: PropTypes.func, + }), + setCourtMartial: PropTypes.func, + viewedIntroPage: PropTypes.bool, +}; + +const mapStateToProps = state => ({ + formResponses: state?.dischargeUpgradeWizard?.duwForm?.form, + viewedIntroPage: state?.dischargeUpgradeWizard?.duwForm?.viewedIntroPage, +}); + +const mapDispatchToProps = { + setCourtMartial: updateCourtMartial, +}; + +export default connect( + mapStateToProps, + mapDispatchToProps, +)(CourtMartial); diff --git a/src/applications/discharge-wizard/components/v2/questions/DischargeMonth.jsx b/src/applications/discharge-wizard/components/v2/questions/DischargeMonth.jsx index dcda3a03f7af..d2dee067b73d 100644 --- a/src/applications/discharge-wizard/components/v2/questions/DischargeMonth.jsx +++ b/src/applications/discharge-wizard/components/v2/questions/DischargeMonth.jsx @@ -42,7 +42,11 @@ const DischargeMonth = ({ const dischargeMonth = formResponses[shortName]; const monthOptions = months.map(month => { return ( - ); diff --git a/src/applications/discharge-wizard/components/v2/questions/DischargeType.jsx b/src/applications/discharge-wizard/components/v2/questions/DischargeType.jsx new file mode 100644 index 000000000000..1fa61cac530e --- /dev/null +++ b/src/applications/discharge-wizard/components/v2/questions/DischargeType.jsx @@ -0,0 +1,79 @@ +import React, { useEffect, useState } from 'react'; +import PropTypes from 'prop-types'; +import { connect } from 'react-redux'; +import { + QUESTION_MAP, + RESPONSES, + SHORT_NAME_MAP, +} from '../../../constants/question-data-map'; +import RadioGroup from './shared/RadioGroup'; +import { updateDischargeType } from '../../../actions'; +import { pageSetup } from '../../../utilities/page-setup'; +import { ROUTES } from '../../../constants'; + +const DischargeType = ({ + formResponses, + setDischargeType, + router, + viewedIntroPage, +}) => { + const [formError, setFormError] = useState(false); + const shortName = SHORT_NAME_MAP.DISCHARGE_TYPE; + const H1 = QUESTION_MAP[shortName]; + const dischargeType = formResponses[shortName]; + const { DISCHARGE_TYPE_1, DISCHARGE_TYPE_2 } = RESPONSES; + + useEffect( + () => { + pageSetup(H1); + }, + [H1], + ); + + useEffect( + () => { + if (!viewedIntroPage) { + router.push(ROUTES.HOME); + } + }, + [router, viewedIntroPage], + ); + + return ( + + ); +}; + +DischargeType.propTypes = { + formResponses: PropTypes.object, + setDischargeType: PropTypes.func, + router: PropTypes.shape({ + push: PropTypes.func, + }), + viewedIntroPage: PropTypes.bool, +}; + +const mapStateToProps = state => ({ + formResponses: state?.dischargeUpgradeWizard?.duwForm?.form, + viewedIntroPage: state?.dischargeUpgradeWizard?.duwForm?.viewedIntroPage, +}); + +const mapDispatchToProps = { + setDischargeType: updateDischargeType, +}; + +export default connect( + mapStateToProps, + mapDispatchToProps, +)(DischargeType); diff --git a/src/applications/discharge-wizard/components/v2/questions/DischargeYear.jsx b/src/applications/discharge-wizard/components/v2/questions/DischargeYear.jsx index ecd2db3921a0..3a67242cfc1f 100644 --- a/src/applications/discharge-wizard/components/v2/questions/DischargeYear.jsx +++ b/src/applications/discharge-wizard/components/v2/questions/DischargeYear.jsx @@ -44,7 +44,7 @@ const DischargeYear = ({ const yearOptions = range(currentYear - 1992).map(i => { const year = currentYear - i; return ( - ); @@ -52,7 +52,7 @@ const DischargeYear = ({ const before1992Key = yearOptions.length + 1; yearOptions.push( - , ); diff --git a/src/applications/discharge-wizard/components/v2/questions/Intention.jsx b/src/applications/discharge-wizard/components/v2/questions/Intention.jsx new file mode 100644 index 000000000000..ef2ea18a0ea4 --- /dev/null +++ b/src/applications/discharge-wizard/components/v2/questions/Intention.jsx @@ -0,0 +1,79 @@ +import React, { useEffect, useState } from 'react'; +import PropTypes from 'prop-types'; +import { connect } from 'react-redux'; +import { + QUESTION_MAP, + RESPONSES, + SHORT_NAME_MAP, +} from '../../../constants/question-data-map'; +import RadioGroup from './shared/RadioGroup'; +import { updateIntention } from '../../../actions'; +import { pageSetup } from '../../../utilities/page-setup'; +import { ROUTES } from '../../../constants'; + +const Intention = ({ + formResponses, + setIntention, + router, + viewedIntroPage, +}) => { + const [formError, setFormError] = useState(false); + const shortName = SHORT_NAME_MAP.INTENTION; + const H1 = QUESTION_MAP[shortName]; + const intention = formResponses[shortName]; + const { INTENTION_1, INTENTION_2 } = RESPONSES; + + useEffect( + () => { + pageSetup(H1); + }, + [H1], + ); + + useEffect( + () => { + if (!viewedIntroPage) { + router.push(ROUTES.HOME); + } + }, + [router, viewedIntroPage], + ); + + return ( + + ); +}; + +Intention.propTypes = { + formResponses: PropTypes.object, + router: PropTypes.shape({ + push: PropTypes.func, + }), + setIntention: PropTypes.func, + viewedIntroPage: PropTypes.bool, +}; + +const mapStateToProps = state => ({ + formResponses: state?.dischargeUpgradeWizard?.duwForm?.form, + viewedIntroPage: state?.dischargeUpgradeWizard?.duwForm?.viewedIntroPage, +}); + +const mapDispatchToProps = { + setIntention: updateIntention, +}; + +export default connect( + mapStateToProps, + mapDispatchToProps, +)(Intention); diff --git a/src/applications/discharge-wizard/components/v2/questions/Reason.jsx b/src/applications/discharge-wizard/components/v2/questions/Reason.jsx new file mode 100644 index 000000000000..70c3378a63bf --- /dev/null +++ b/src/applications/discharge-wizard/components/v2/questions/Reason.jsx @@ -0,0 +1,95 @@ +import React, { useEffect, useState } from 'react'; +import PropTypes from 'prop-types'; +import { connect } from 'react-redux'; +import { + QUESTION_MAP, + RESPONSES, + SHORT_NAME_MAP, +} from '../../../constants/question-data-map'; +import RadioGroup from './shared/RadioGroup'; +import { updateReason } from '../../../actions'; +import { pageSetup } from '../../../utilities/page-setup'; +import { ROUTES } from '../../../constants'; + +const Reason = ({ formResponses, setReason, router, viewedIntroPage }) => { + const [formError, setFormError] = useState(false); + const shortName = SHORT_NAME_MAP.REASON; + const H1 = QUESTION_MAP[shortName]; + const reason = formResponses[shortName]; + const hint = + 'Note: If more than one of these descriptions matches your situation, choose the one that started the events that led to your discharge. For example, if you sustained a traumatic brain injury, which led to posttraumatic stress disorder (PTSD), choose number 2.'; + const { + REASON_1, + REASON_2, + REASON_3, + REASON_4, + REASON_5, + REASON_6, + REASON_7, + REASON_8, + } = RESPONSES; + + useEffect( + () => { + pageSetup(H1); + }, + [H1], + ); + + useEffect( + () => { + if (!viewedIntroPage) { + router.push(ROUTES.HOME); + } + }, + [router, viewedIntroPage], + ); + + return ( + + ); +}; + +Reason.propTypes = { + formResponses: PropTypes.object, + router: PropTypes.shape({ + push: PropTypes.func, + }), + setReason: PropTypes.func, + viewedIntroPage: PropTypes.bool, +}; + +const mapStateToProps = state => ({ + formResponses: state?.dischargeUpgradeWizard?.duwForm?.form, + viewedIntroPage: state?.dischargeUpgradeWizard?.duwForm?.viewedIntroPage, +}); + +const mapDispatchToProps = { + setReason: updateReason, +}; + +export default connect( + mapStateToProps, + mapDispatchToProps, +)(Reason); diff --git a/src/applications/discharge-wizard/components/v2/questions/shared/Dropdown.jsx b/src/applications/discharge-wizard/components/v2/questions/shared/Dropdown.jsx index 6433f3fa53f7..435bbee4112d 100644 --- a/src/applications/discharge-wizard/components/v2/questions/shared/Dropdown.jsx +++ b/src/applications/discharge-wizard/components/v2/questions/shared/Dropdown.jsx @@ -61,7 +61,7 @@ const Dropdown = ({ } setFormError(false); - navigateForward(shortName, formValue, router); + navigateForward(shortName, formResponses, router); } }; diff --git a/src/applications/discharge-wizard/components/v2/questions/shared/RadioGroup.jsx b/src/applications/discharge-wizard/components/v2/questions/shared/RadioGroup.jsx index a4f2a183e413..e90adfdd6ac6 100644 --- a/src/applications/discharge-wizard/components/v2/questions/shared/RadioGroup.jsx +++ b/src/applications/discharge-wizard/components/v2/questions/shared/RadioGroup.jsx @@ -19,6 +19,7 @@ const RadioGroup = ({ formError, formResponses, formValue, + hint, H1, responses, router, @@ -42,7 +43,7 @@ const RadioGroup = ({ } setFormError(false); - navigateForward(shortName, formValue, router, formResponses); + navigateForward(shortName, formResponses, router); } }; @@ -76,15 +77,14 @@ const RadioGroup = ({ ); }); }; - return ( <> onValueChange(e.detail.value)} onLoad={applyFocus('duw-radio', headerHasFocused, setHeaderHasFocused)} @@ -117,6 +117,7 @@ RadioGroup.propTypes = { updateCleanedFormStore: PropTypes.func.isRequired, valueSetter: PropTypes.func.isRequired, formValue: PropTypes.string, + hint: PropTypes.string, }; const mapDispatchToProps = { diff --git a/src/applications/discharge-wizard/constants/display-conditions.js b/src/applications/discharge-wizard/constants/display-conditions.js new file mode 100644 index 000000000000..8cf3eed25223 --- /dev/null +++ b/src/applications/discharge-wizard/constants/display-conditions.js @@ -0,0 +1,73 @@ +import { range } from 'lodash'; +import { RESPONSES } from './question-data-map'; + +const get15YearsPast = () => `${new Date().getFullYear() - 15}`; +const currentYear = new Date().getFullYear(); +const yearResponses = range(currentYear - 1992).map(i => { + const year = currentYear - i; + return year.toString(); +}); +const { + ARMY, + NAVY, + AIR_FORCE, + COAST_GUARD, + MARINE_CORPS, + REASON_1, + REASON_2, + REASON_3, + REASON_4, + REASON_5, + // REASON_8, + REASON_6, + REASON_7, + // INTENTION_1, + // INTENTION_2, + // COURT_MARTIAL_1, + // COURT_MARTIAL_2, + // COURT_MARTIAL_3, + // DISCHARGE_TYPE_1, + // DISCHARGE_TYPE_2, +} = RESPONSES; + +export const DISPLAY_CONDITIONS = Object.freeze({ + SERVICE_BRANCH: {}, + DISCHARGE_YEAR: { + SERVICE_BRANCH: [ARMY, NAVY, AIR_FORCE, COAST_GUARD, MARINE_CORPS], + }, + DISCHARGE_MONTH: { + SERVICE_BRANCH: [ARMY, NAVY, AIR_FORCE, COAST_GUARD, MARINE_CORPS], + DISCHARGE_YEAR: [get15YearsPast()], + }, + REASON: { + SERVICE_BRANCH: [ARMY, NAVY, AIR_FORCE, COAST_GUARD, MARINE_CORPS], + DISCHARGE_YEAR: yearResponses, + DISCHARGE_MONTH: [], + }, + DISCHARGE_TYPE: { + SERVICE_BRANCH: [ARMY, NAVY, AIR_FORCE, COAST_GUARD, MARINE_CORPS], + DISCHARGE_YEAR: yearResponses, + DISCHARGE_MONTH: [], + REASON: [REASON_3], + }, + INTENTION: { + SERVICE_BRANCH: [ARMY, NAVY, AIR_FORCE, COAST_GUARD, MARINE_CORPS], + DISCHARGE_YEAR: yearResponses, + DISCHARGE_MONTH: [], + REASON: [REASON_1, REASON_2, REASON_3, REASON_4, REASON_6, REASON_7], + }, + COURT_MARTIAL: { + SERVICE_BRANCH: [ARMY, NAVY, AIR_FORCE, COAST_GUARD, MARINE_CORPS], + DISCHARGE_YEAR: yearResponses, + DISCHARGE_MONTH: [], + REASON: [ + REASON_5, + REASON_1, + REASON_2, + REASON_3, + REASON_4, + REASON_6, + REASON_7, + ], + }, +}); diff --git a/src/applications/discharge-wizard/constants/index.js b/src/applications/discharge-wizard/constants/index.js index 195e85a34a53..d595a69a22bf 100644 --- a/src/applications/discharge-wizard/constants/index.js +++ b/src/applications/discharge-wizard/constants/index.js @@ -100,21 +100,33 @@ import { SHORT_NAME_MAP } from './question-data-map'; export const DUW_VIEWED_INTRO_PAGE = 'discharge-upgrade-wizard/DUW_VIEWED_INTRO_PAGE'; +export const DUW_UPDATE_FORM_STORE = + 'discharge-upgrade-wizard/DUW_UPDATE_FORM_STORE'; export const DUW_UPDATE_SERVICE_BRANCH = 'discharge-upgrade-wizard/DUW_UPDATE_SERVICE_BRANCH'; export const DUW_UPDATE_DISCHARGE_YEAR = 'discharge-upgrade-wizard/DUW_UPDATE_DISCHARGE_YEAR'; export const DUW_UPDATE_DISCHARGE_MONTH = 'discharge-upgrade-wizard/DUW_UPDATE_DISCHARGE_MONTH'; -export const DUW_UPDATE_FORM_STORE = - 'discharge-upgrade-wizard/DUW_UPDATE_FORM_STORE'; +export const DUW_UPDATE_REASON = 'discharge-upgrade-wizard/DUW_UPDATE_REASON'; +export const DUW_UPDATE_DISCHARGE_TYPE = + 'discharge-upgrade-wizard/DUW_UPDATE_DISCHARGE_TYPE'; +export const DUW_UPDATE_COURT_MARTIAL = + 'discharge-upgrade-wizard/DUW_UPDATE_COURT_MARTIAL'; +export const DUW_UPDATE_INTENTION = + 'discharge-upgrade-wizard/DUW_UPDATE_INTENTION'; export const ROUTES = Object.freeze({ HOME: 'introduction', SERVICE_BRANCH: 'service-branch', DISCHARGE_YEAR: 'discharge-year', DISCHARGE_MONTH: 'discharge-month', - DISCHARGE_REASON: 'discharge-reason', + REASON: 'reason', + DISCHARGE_TYPE: 'discharge-type', + COURT_MARTIAL: 'court-martial', + INTENTION: 'intention', + PREVIOUS_APPLICATION: 'previous-application', + PREVIOUS_APPLICATION_TYPE: 'previous-application-type', RESULT: 'result', }); @@ -122,13 +134,23 @@ export const questionsToClearMap = Object.freeze({ SERVICE_BRANCH: [], DISCHARGE_YEAR: [SHORT_NAME_MAP.DISCHARGE_MONTH], DISCHARGE_MONTH: [], - DISCHARGE_REASON: [], + REASON: [ + SHORT_NAME_MAP.DISCHARGE_TYPE, + SHORT_NAME_MAP.COURT_MARTIAL, + SHORT_NAME_MAP.INTENTION, + SHORT_NAME_MAP.PREVIOUS_APPLICATION, + ], + DISCHARGE_TYPE: [], + COURT_MARTIAL: [], + INTENTION: [], + PREVIOUS_APPLICATION: [], }); export const errorTextMap = Object.freeze({ SERVICE_BRANCH: 'Select a branch', DISCHARGE_YEAR: 'Select a year', DISCHARGE_MONTH: 'Select a month', + REASON: 'Select a reason', }); export const labelTextMap = Object.freeze({ diff --git a/src/applications/discharge-wizard/constants/question-data-map.js b/src/applications/discharge-wizard/constants/question-data-map.js index 5c91ed3cfa2c..f5186019f5b8 100644 --- a/src/applications/discharge-wizard/constants/question-data-map.js +++ b/src/applications/discharge-wizard/constants/question-data-map.js @@ -3,6 +3,11 @@ export const QUESTION_MAP = Object.freeze({ SERVICE_BRANCH: 'What was your branch of service?', DISCHARGE_YEAR: 'What year were you discharged from the military?', DISCHARGE_MONTH: 'What month were you discharged?', + REASON: 'Tell us why you want to change your discharge paperwork.', + DISCHARGE_TYPE: `What's your character of discharge?`, + INTENTION: + 'Do you want to change your name, discharge date, or something written in the "other remarks" section of your DD214?', + COURT_MARTIAL: 'Was your discharge the outcome of a general court-martial?', }); export const SHORT_NAME_MAP = Object.freeze({ @@ -10,7 +15,12 @@ export const SHORT_NAME_MAP = Object.freeze({ SERVICE_BRANCH: 'SERVICE_BRANCH', DISCHARGE_YEAR: 'DISCHARGE_YEAR', DISCHARGE_MONTH: 'DISCHARGE_MONTH', - DISCHARGE_REASON: 'DISCHARGE_REASON', + REASON: 'REASON', + DISCHARGE_TYPE: 'DISCHARGE_TYPE', + INTENTION: 'INTENTION', + COURT_MARTIAL: 'COURT_MARTIAL', + PREVIOUS_APPLICATION_TYPE: 'PREVIOUS_APPLICATION_TYPE', + PREVIOUS_APPLICATION: 'PREVIOUS_APPLICATION', }); export const RESPONSES = Object.freeze({ @@ -19,4 +29,30 @@ export const RESPONSES = Object.freeze({ AIR_FORCE: 'Air Force', COAST_GUARD: 'Coast Guard', MARINE_CORPS: 'Marine Corps', + REASON_1: + 'I suffered from undiagnosed, misdiagnosed, or untreated posttraumatic stress disorder (PTSD), or another mental health condition, during my service. I was discharged for reasons related to this condition.', + REASON_2: + 'I suffered from an undiagnosed, misdiagnosed, or untreated traumatic brain injury (TBI) during my service. I was discharged for reasons related to this condition.', + REASON_3: + 'I was discharged due to my sexual orientation (including under the Don’t Ask, Don’t Tell policy).', + REASON_4: + 'I experienced sexual assault or harassment during my service. I was discharged for reasons related to this experience.', + REASON_5: + 'I’m transgender, and my discharge shows my birth name instead of my current name.', + REASON_8: + 'I received a DD215 that shows my discharge upgrade or correction. But I want an updated DD214.', + REASON_6: 'My discharge paperwork has another kind of error.', + REASON_7: + 'My discharge is unjust, and it isn’t related to any of the reasons listed here.', + INTENTION_1: `Yes, I want to change my name, discharge date, or something written in the "other remarks" section of my DD214. (This isn't common.)`, + INTENTION_2: + 'No, I want to change only my characterization of discharge, re-enlistment code, separation code, or narrative reason for discharge.', + COURT_MARTIAL_1: + 'Yes, my discharge was the outcome of a general court-martial.', + COURT_MARTIAL_2: + 'No, my discharge was administrative or the outcome of a special or summary court-martial.', + COURT_MARTIAL_3: `I'm not sure.`, + DISCHARGE_TYPE_1: + 'My discharge is honorable or general under honorable conditions. I want to change only my narrative reason for discharge, separation code, or re-enlistment code.', + DISCHARGE_TYPE_2: `My discharge isn't honorable or under honorable conditions.`, }); diff --git a/src/applications/discharge-wizard/reducers/v2/discharge-upgrade-wizard.js b/src/applications/discharge-wizard/reducers/v2/discharge-upgrade-wizard.js index 38fb9e671f9a..6b82023d4e53 100644 --- a/src/applications/discharge-wizard/reducers/v2/discharge-upgrade-wizard.js +++ b/src/applications/discharge-wizard/reducers/v2/discharge-upgrade-wizard.js @@ -3,13 +3,25 @@ import { DUW_UPDATE_SERVICE_BRANCH, DUW_UPDATE_DISCHARGE_YEAR, DUW_UPDATE_DISCHARGE_MONTH, + DUW_UPDATE_REASON, + DUW_UPDATE_DISCHARGE_TYPE, + DUW_UPDATE_INTENTION, + DUW_UPDATE_COURT_MARTIAL, DUW_UPDATE_FORM_STORE, } from '../../constants'; import { SHORT_NAME_MAP } from '../../constants/question-data-map'; import { createFormStore, updateFormValue } from './utilities'; -const { SERVICE_BRANCH, DISCHARGE_YEAR, DISCHARGE_MONTH } = SHORT_NAME_MAP; +const { + SERVICE_BRANCH, + DISCHARGE_YEAR, + DISCHARGE_MONTH, + REASON, + DISCHARGE_TYPE, + COURT_MARTIAL, + INTENTION, +} = SHORT_NAME_MAP; const initialState = { currentPage: SHORT_NAME_MAP.HOME, @@ -25,6 +37,14 @@ export default (state = initialState, action) => { return updateFormValue(DISCHARGE_YEAR, state, action); case DUW_UPDATE_DISCHARGE_MONTH: return updateFormValue(DISCHARGE_MONTH, state, action); + case DUW_UPDATE_REASON: + return updateFormValue(REASON, state, action); + case DUW_UPDATE_DISCHARGE_TYPE: + return updateFormValue(DISCHARGE_TYPE, state, action); + case DUW_UPDATE_COURT_MARTIAL: + return updateFormValue(COURT_MARTIAL, state, action); + case DUW_UPDATE_INTENTION: + return updateFormValue(INTENTION, state, action); case DUW_VIEWED_INTRO_PAGE: return { ...state, diff --git a/src/applications/discharge-wizard/routes.jsx b/src/applications/discharge-wizard/routes.jsx index 5aa0f4ec161e..fb5bc002c3b2 100644 --- a/src/applications/discharge-wizard/routes.jsx +++ b/src/applications/discharge-wizard/routes.jsx @@ -10,6 +10,10 @@ import HomePage from './components/v2/Homepage'; import ServiceBranch from './components/v2/questions/ServiceBranch'; import DischargeYear from './components/v2/questions/DischargeYear'; import DischargeMonth from './components/v2/questions/DischargeMonth'; +import Reason from './components/v2/questions/Reason'; +import DischargeType from './components/v2/questions/DischargeType'; +import CourtMartial from './components/v2/questions/CourtMartial'; +import Intention from './components/v2/questions/Intention'; const envChildRoutes = environment.isProduction() ? [ @@ -27,6 +31,10 @@ const envChildRoutes = environment.isProduction() { path: 'service-branch', component: ServiceBranch }, { path: 'discharge-year', component: DischargeYear }, { path: 'discharge-month', component: DischargeMonth }, + { path: 'reason', component: Reason }, + { path: 'discharge-type', component: DischargeType }, + { path: 'court-martial', component: CourtMartial }, + { path: 'intention', component: Intention }, ]; const routes = { path: '/', diff --git a/src/applications/discharge-wizard/sass/discharge-wizard.scss b/src/applications/discharge-wizard/sass/discharge-wizard.scss index 7e48a838c16e..5326d8dcf1f9 100644 --- a/src/applications/discharge-wizard/sass/discharge-wizard.scss +++ b/src/applications/discharge-wizard/sass/discharge-wizard.scss @@ -119,7 +119,4 @@ $formation-image-path: "~@department-of-veterans-affairs/formation/assets/img"; } .discharge-wizard-v2 { //v2 styles - #form-description { - display: none; - } } \ No newline at end of file diff --git a/src/applications/discharge-wizard/tests/v2/CourtMartial.unit.spec.js b/src/applications/discharge-wizard/tests/v2/CourtMartial.unit.spec.js new file mode 100644 index 000000000000..b1872f3f8d5f --- /dev/null +++ b/src/applications/discharge-wizard/tests/v2/CourtMartial.unit.spec.js @@ -0,0 +1,80 @@ +import React from 'react'; +import { Provider } from 'react-redux'; +import { render } from '@testing-library/react'; +import { expect } from 'chai'; +import sinon from 'sinon'; +import { ROUTES } from '../../constants'; + +import CourtMartial from '../../components/v2/questions/CourtMartial'; + +const mockStoreStandard = { + getState: () => ({ + dischargeUpgradeWizard: { + duwForm: { + form: {}, + viewedIntroPage: true, + }, + }, + }), + subscribe: () => {}, + dispatch: () => {}, +}; + +const mockStoreNoIntroPage = { + getState: () => ({ + dischargeUpgradeWizard: { + duwForm: { + form: {}, + viewedIntroPage: false, + }, + }, + }), + subscribe: () => {}, + dispatch: () => {}, +}; + +const pushStub = sinon.stub(); + +const propsStandard = { + formResponses: {}, + setCourtMartial: () => {}, + router: { + push: pushStub, + }, + viewedIntroPage: true, +}; + +const propsNoIntroPage = { + formResponses: {}, + setCourtMartial: () => {}, + router: { + push: pushStub, + }, + viewedIntroPage: false, +}; + +describe('Court Martial Page', () => { + afterEach(() => { + pushStub.resetHistory(); + }); + + it('should correctly load the court martial page in the standard flow', () => { + const screen = render( + + + , + ); + + expect(screen.getByTestId('duw-court_martial')).to.exist; + }); + + it('should redirect to home when the intro page has not been viewed', () => { + render( + + + , + ); + + expect(pushStub.withArgs(ROUTES.HOME).called).to.be.true; + }); +}); diff --git a/src/applications/discharge-wizard/tests/v2/DischargeMonth.unit.spec.js b/src/applications/discharge-wizard/tests/v2/DischargeMonth.unit.spec.js index a12798db674a..cb071b6b2636 100644 --- a/src/applications/discharge-wizard/tests/v2/DischargeMonth.unit.spec.js +++ b/src/applications/discharge-wizard/tests/v2/DischargeMonth.unit.spec.js @@ -3,7 +3,6 @@ import { Provider } from 'react-redux'; import { render } from '@testing-library/react'; import { expect } from 'chai'; import sinon from 'sinon'; -import { RESPONSES } from '../../constants/question-data-map'; import { ROUTES } from '../../constants'; import DischargeMonth from '../../components/v2/questions/DischargeMonth'; @@ -12,11 +11,7 @@ const mockStoreStandard = { getState: () => ({ dischargeUpgradeWizard: { duwForm: { - form: { - SERVICE_BRANCH: null, - DISCHARGE_YEAR: null, - DISCHARGE_MONTH: null, - }, + form: {}, viewedIntroPage: true, }, }, @@ -29,11 +24,7 @@ const mockStoreNoIntroPage = { getState: () => ({ dischargeUpgradeWizard: { duwForm: { - form: { - SERVICE_BRANCH: null, - DISCHARGE_YEAR: null, - DISCHARGE_MONTH: null, - }, + form: {}, viewedIntroPage: false, }, }, @@ -45,11 +36,7 @@ const mockStoreNoIntroPage = { const pushStub = sinon.stub(); const propsStandard = { - formResponses: { - SERVICE_BRANCH: RESPONSES.ARMY, - DISCHARGE_YEAR: 2009, - DISCHARGE_MONTH: null, - }, + formResponses: {}, setDischargeMonth: () => {}, router: { push: pushStub, @@ -58,11 +45,7 @@ const propsStandard = { }; const propsNoIntroPage = { - formResponses: { - SERVICE_BRANCH: RESPONSES.ARMY, - DISCHARGE_YEAR: null, - DISCHARGE_MONTH: null, - }, + formResponses: {}, setDischargeMonth: () => {}, router: { push: pushStub, diff --git a/src/applications/discharge-wizard/tests/v2/DischargeType.unit.spec.js b/src/applications/discharge-wizard/tests/v2/DischargeType.unit.spec.js new file mode 100644 index 000000000000..07e09c77ea10 --- /dev/null +++ b/src/applications/discharge-wizard/tests/v2/DischargeType.unit.spec.js @@ -0,0 +1,80 @@ +import React from 'react'; +import { Provider } from 'react-redux'; +import { render } from '@testing-library/react'; +import { expect } from 'chai'; +import sinon from 'sinon'; +import { ROUTES } from '../../constants'; + +import DischargeType from '../../components/v2/questions/DischargeType'; + +const mockStoreStandard = { + getState: () => ({ + dischargeUpgradeWizard: { + duwForm: { + form: {}, + viewedIntroPage: true, + }, + }, + }), + subscribe: () => {}, + dispatch: () => {}, +}; + +const mockStoreNoIntroPage = { + getState: () => ({ + dischargeUpgradeWizard: { + duwForm: { + form: {}, + viewedIntroPage: false, + }, + }, + }), + subscribe: () => {}, + dispatch: () => {}, +}; + +const pushStub = sinon.stub(); + +const propsStandard = { + formResponses: {}, + setCourtMartial: () => {}, + router: { + push: pushStub, + }, + viewedIntroPage: true, +}; + +const propsNoIntroPage = { + formResponses: {}, + setCourtMartial: () => {}, + router: { + push: pushStub, + }, + viewedIntroPage: false, +}; + +describe('Discharge Type Page', () => { + afterEach(() => { + pushStub.resetHistory(); + }); + + it('should correctly load the discharge type page in the standard flow', () => { + const screen = render( + + + , + ); + + expect(screen.getByTestId('duw-discharge_type')).to.exist; + }); + + it('should redirect to home when the intro page has not been viewed', () => { + render( + + + , + ); + + expect(pushStub.withArgs(ROUTES.HOME).called).to.be.true; + }); +}); diff --git a/src/applications/discharge-wizard/tests/v2/DischargeYear.unit.spec.js b/src/applications/discharge-wizard/tests/v2/DischargeYear.unit.spec.js index 46f9a0e4e293..6f70ee624de7 100644 --- a/src/applications/discharge-wizard/tests/v2/DischargeYear.unit.spec.js +++ b/src/applications/discharge-wizard/tests/v2/DischargeYear.unit.spec.js @@ -3,7 +3,6 @@ import { Provider } from 'react-redux'; import { render } from '@testing-library/react'; import { expect } from 'chai'; import sinon from 'sinon'; -import { RESPONSES } from '../../constants/question-data-map'; import { ROUTES } from '../../constants'; import DischargeYear from '../../components/v2/questions/DischargeYear'; @@ -12,11 +11,7 @@ const mockStoreStandard = { getState: () => ({ dischargeUpgradeWizard: { duwForm: { - form: { - SERVICE_BRANCH: null, - DISCHARGE_YEAR: null, - DISCHARGE_MONTH: null, - }, + form: {}, viewedIntroPage: true, }, }, @@ -29,11 +24,7 @@ const mockStoreNoIntroPage = { getState: () => ({ dischargeUpgradeWizard: { duwForm: { - form: { - SERVICE_BRANCH: null, - DISCHARGE_YEAR: null, - DISCHARGE_MONTH: null, - }, + form: {}, viewedIntroPage: false, }, }, @@ -45,11 +36,7 @@ const mockStoreNoIntroPage = { const pushStub = sinon.stub(); const propsStandard = { - formResponses: { - SERVICE_BRANCH: RESPONSES.ARMY, - DISCHARGE_YEAR: null, - DISCHARGE_MONTH: null, - }, + formResponses: {}, setDischargeYear: () => {}, router: { push: pushStub, @@ -58,11 +45,7 @@ const propsStandard = { }; const propsNoIntroPage = { - formResponses: { - SERVICE_BRANCH: RESPONSES.ARMY, - DISCHARGE_YEAR: null, - DISCHARGE_MONTH: null, - }, + formResponses: {}, setDischargeYear: () => {}, router: { push: pushStub, diff --git a/src/applications/discharge-wizard/tests/v2/Intention.unit.spec.js b/src/applications/discharge-wizard/tests/v2/Intention.unit.spec.js new file mode 100644 index 000000000000..2fef8e61f881 --- /dev/null +++ b/src/applications/discharge-wizard/tests/v2/Intention.unit.spec.js @@ -0,0 +1,80 @@ +import React from 'react'; +import { Provider } from 'react-redux'; +import { render } from '@testing-library/react'; +import { expect } from 'chai'; +import sinon from 'sinon'; +import { ROUTES } from '../../constants'; + +import Intention from '../../components/v2/questions/Intention'; + +const mockStoreStandard = { + getState: () => ({ + dischargeUpgradeWizard: { + duwForm: { + form: {}, + viewedIntroPage: true, + }, + }, + }), + subscribe: () => {}, + dispatch: () => {}, +}; + +const mockStoreNoIntroPage = { + getState: () => ({ + dischargeUpgradeWizard: { + duwForm: { + form: {}, + viewedIntroPage: false, + }, + }, + }), + subscribe: () => {}, + dispatch: () => {}, +}; + +const pushStub = sinon.stub(); + +const propsStandard = { + formResponses: {}, + setIntention: () => {}, + router: { + push: pushStub, + }, + viewedIntroPage: true, +}; + +const propsNoIntroPage = { + formResponses: {}, + setIntention: () => {}, + router: { + push: pushStub, + }, + viewedIntroPage: false, +}; + +describe('Intention Page', () => { + afterEach(() => { + pushStub.resetHistory(); + }); + + it('should correctly load the intention page in the standard flow', () => { + const screen = render( + + + , + ); + + expect(screen.getByTestId('duw-intention')).to.exist; + }); + + it('should redirect to home when the intro page has not been viewed', () => { + render( + + + , + ); + + expect(pushStub.withArgs(ROUTES.HOME).called).to.be.true; + }); +}); diff --git a/src/applications/discharge-wizard/tests/v2/Reason.unit.spec.js b/src/applications/discharge-wizard/tests/v2/Reason.unit.spec.js new file mode 100644 index 000000000000..500b37bdb300 --- /dev/null +++ b/src/applications/discharge-wizard/tests/v2/Reason.unit.spec.js @@ -0,0 +1,80 @@ +import React from 'react'; +import { Provider } from 'react-redux'; +import { render } from '@testing-library/react'; +import { expect } from 'chai'; +import sinon from 'sinon'; +import { ROUTES } from '../../constants'; + +import Reason from '../../components/v2/questions/Reason'; + +const mockStoreStandard = { + getState: () => ({ + dischargeUpgradeWizard: { + duwForm: { + form: {}, + viewedIntroPage: true, + }, + }, + }), + subscribe: () => {}, + dispatch: () => {}, +}; + +const mockStoreNoIntroPage = { + getState: () => ({ + dischargeUpgradeWizard: { + duwForm: { + form: {}, + viewedIntroPage: false, + }, + }, + }), + subscribe: () => {}, + dispatch: () => {}, +}; + +const pushStub = sinon.stub(); + +const propsStandard = { + formResponses: {}, + setReason: () => {}, + router: { + push: pushStub, + }, + viewedIntroPage: true, +}; + +const propsNoIntroPage = { + formResponses: {}, + setReason: () => {}, + router: { + push: pushStub, + }, + viewedIntroPage: false, +}; + +describe('Reason Page', () => { + afterEach(() => { + pushStub.resetHistory(); + }); + + it('should correctly load the reason page in the standard flow', () => { + const screen = render( + + + , + ); + + expect(screen.getByTestId('duw-reason')).to.exist; + }); + + it('should redirect to home when the intro page has not been viewed', () => { + render( + + + , + ); + + expect(pushStub.withArgs(ROUTES.HOME).called).to.be.true; + }); +}); diff --git a/src/applications/discharge-wizard/tests/v2/cypress/discharge-upgrade-wizard.cypress.spec.js b/src/applications/discharge-wizard/tests/v2/cypress/discharge-upgrade-wizard.cypress.spec.js new file mode 100644 index 000000000000..4701ebdd5688 --- /dev/null +++ b/src/applications/discharge-wizard/tests/v2/cypress/discharge-upgrade-wizard.cypress.spec.js @@ -0,0 +1,57 @@ +import * as h from './helpers'; +import { ROUTES } from '../../../constants'; +import { SHORT_NAME_MAP } from '../../../constants/question-data-map'; + +xdescribe('Discharge Upgrade Wizard', () => { + describe('Base navigation', () => { + it('navigates through the flow forward successfully', () => { + cy.visit(`${h.ROOT}/introduction1`); + + // Home + h.verifyUrl(`${ROUTES.HOME}1`); + cy.injectAxeThenAxeCheck(); + h.clickStart(); + + // SERVICE_BRANCH + h.verifyUrl(ROUTES.SERVICE_BRANCH); + h.selectRadio(h.SERVICE_BRANCH_INPUT, 3); + h.clickContinue(); + + // DISCHARGE_YEAR + h.verifyUrl(ROUTES.DISCHARGE_YEAR); + h.selectDropdown( + h.DISCHARGE_YEAR_INPUT, + SHORT_NAME_MAP.DISCHARGE_YEAR, + h.get15YearsPast(), + ); + h.clickContinue(); + + // DISCHARGE_MONTH + h.verifyUrl(ROUTES.DISCHARGE_MONTH); + h.selectDropdown( + h.DISCHARGE_MONTH_INPUT, + SHORT_NAME_MAP.DISCHARGE_MONTH, + 3, + ); + h.clickContinue(); + + // DISCHARGE_REASON + h.verifyUrl(ROUTES.REASON); + h.selectRadio(h.REASON_INPUT, 3); + h.clickContinue(); + + // INTENTION + h.verifyUrl(ROUTES.INTENTION); + h.selectRadio(h.INTENTION_INPUT, 1); + h.clickContinue(); + + // COURT_MARTIAL + h.verifyUrl(ROUTES.COURT_MARTIAL); + h.selectRadio(h.COURT_MARTIAL_INPUT, 1); + h.clickContinue(); + + // PREVIOUS_APPLICATION + h.verifyUrl(ROUTES.PREVIOUS_APPLICATION); + }); + }); +}); diff --git a/src/applications/discharge-wizard/tests/v2/cypress/helpers.js b/src/applications/discharge-wizard/tests/v2/cypress/helpers.js new file mode 100644 index 000000000000..68468e168769 --- /dev/null +++ b/src/applications/discharge-wizard/tests/v2/cypress/helpers.js @@ -0,0 +1,55 @@ +export const ROOT = '/discharge-upgrade-instructions'; +export const START_ID = 'duw-start-form'; + +export const SERVICE_BRANCH_INPUT = 'duw-service_branch'; +export const DISCHARGE_YEAR_INPUT = 'duw-discharge_year'; +export const DISCHARGE_MONTH_INPUT = 'duw-discharge_month'; +export const REASON_INPUT = 'duw-reason'; +export const INTENTION_INPUT = 'duw-intention'; +export const COURT_MARTIAL_INPUT = 'duw-court_martial'; + +export const clickStart = () => + cy + .findByTestId(START_ID) + .should('be.visible') + .click(); + +export const verifyUrl = link => cy.url().should('contain', `${ROOT}/${link}`); + +export const get15YearsPast = () => (new Date().getFullYear() - 15).toString(); + +export const verifyElement = selector => + cy.findByTestId(selector).should('exist'); + +export const selectRadio = (selector, index) => + cy + .findByTestId(selector) + .should('exist') + .get('[data-testid=va-radio-option]') + .eq(index) + .click(); + +export const selectDropdown = (selector, shortName, option) => + cy + .findByTestId(selector) + .shadow() + .get(`select[name=${shortName}_dropdown]`) + .select(option, { force: true }); + +export const clickBack = () => + cy + .findByTestId('duw-buttonPair') + .shadow() + .get('va-button') + .first() + .should('be.visible') + .click(); + +export const clickContinue = () => + cy + .findByTestId('duw-buttonPair') + .shadow() + .get('va-button') + .eq(1) + .should('be.visible') + .click(); diff --git a/src/applications/discharge-wizard/tests/v2/display-logic.unit.spec.js b/src/applications/discharge-wizard/tests/v2/display-logic.unit.spec.js index be557a29282d..dded20f127cb 100644 --- a/src/applications/discharge-wizard/tests/v2/display-logic.unit.spec.js +++ b/src/applications/discharge-wizard/tests/v2/display-logic.unit.spec.js @@ -7,14 +7,14 @@ import { RESPONSES, SHORT_NAME_MAP } from '../../constants/question-data-map'; describe('utilities: display logic', () => { describe('navigateForward', () => { describe('routing to discharge year', () => { - const formValue = RESPONSES.ARMY; + const formResponses = { SERVICE_BRANCH: RESPONSES.ARMY }; const router = { push: sinon.spy(), }; it('SERVICE_BRANCH: should correctly route to the next question', () => { - navigateForward(SHORT_NAME_MAP.SERVICE_BRANCH, formValue, router); + navigateForward(SHORT_NAME_MAP.SERVICE_BRANCH, formResponses, router); expect(router.push.firstCall.calledWith(ROUTES.DISCHARGE_YEAR)).to.be .true; }); diff --git a/src/applications/discharge-wizard/utilities/display-logic-questions.js b/src/applications/discharge-wizard/utilities/display-logic-questions.js index 09dbefb5ba0e..dc98449b7bb6 100644 --- a/src/applications/discharge-wizard/utilities/display-logic-questions.js +++ b/src/applications/discharge-wizard/utilities/display-logic-questions.js @@ -1,25 +1,44 @@ -import { ROUTES } from '../constants'; +import { DISPLAY_CONDITIONS } from '../constants/display-conditions'; import { SHORT_NAME_MAP } from '../constants/question-data-map'; -export const nextQuestionRoute = (currentQuestion, answer) => { - let nextRoute; +/** ================================================================ + * Make a roadmap (in display order) of SHORT_NAMEs that display in the flow + * + */ +export const makeRoadmap = () => { + return Object.keys(SHORT_NAME_MAP); +}; + +/** ================================================================ + * Check form responses to see if they match the requirements in DISPLAY_CONDITIONS + * + * @param {array} requiredResponses Example: [NO, NOT_SURE] + * @param {string} formResponse - answer in the store + */ +export const responseMatchesRequired = (requiredResponses, formResponse) => { + return requiredResponses?.includes(formResponse); +}; + +/** ================================================================ + * Function for evaluating whether a question should display + * + * @param {string} SHORT_NAME - name for the current question + * @param {object} formResponses - all answers in the store + */ +export const displayConditionsMet = (SHORT_NAME, formResponses) => { + const displayConditionsForShortName = DISPLAY_CONDITIONS[SHORT_NAME]; + const questionRequirements = Object.keys(displayConditionsForShortName); + + for (const questionShortName of questionRequirements) { + const formResponse = formResponses?.[questionShortName]; + const requiredResponses = displayConditionsForShortName[questionShortName]; - switch (currentQuestion) { - case SHORT_NAME_MAP.SERVICE_BRANCH: - nextRoute = SHORT_NAME_MAP.DISCHARGE_YEAR; - break; - case SHORT_NAME_MAP.DISCHARGE_YEAR: - if (answer === `${new Date().getFullYear() - 15}`) { - nextRoute = SHORT_NAME_MAP.DISCHARGE_MONTH; - } else { - nextRoute = SHORT_NAME_MAP.DISCHARGE_REASON; - } - break; - case SHORT_NAME_MAP.DISCHARGE_MONTH: - nextRoute = SHORT_NAME_MAP.DISCHARGE_REASON; - break; - default: - return ROUTES.RESULTS; + if ( + !responseMatchesRequired(requiredResponses, formResponse) && + requiredResponses.length // This is for display conditions that can be any value ex: branch of service, year, month + ) { + return false; + } } - return nextRoute; + return true; }; diff --git a/src/applications/discharge-wizard/utilities/page-navigation.js b/src/applications/discharge-wizard/utilities/page-navigation.js index ebe108667f5b..cd13a893e6ea 100644 --- a/src/applications/discharge-wizard/utilities/page-navigation.js +++ b/src/applications/discharge-wizard/utilities/page-navigation.js @@ -1,12 +1,57 @@ -import { nextQuestionRoute } from './display-logic-questions'; -import { pushToRoute } from './shared'; - -export const navigateForward = (SHORT_NAME, formValue, router) => { - const nextRoute = nextQuestionRoute(SHORT_NAME, formValue); - - pushToRoute(nextRoute, router); -}; +import { DISPLAY_CONDITIONS } from '../constants/display-conditions'; +import { displayConditionsMet, makeRoadmap } from './display-logic-questions'; +import { printErrorMessage, pushToRoute } from './shared'; export const navigateBackward = router => { router.goBack(); }; + +/** ================================================================ + * Responsible for determining next question in flow, or redirecting to a results screen + * + * @param {string} SHORT_NAME - name for the current question + * @param {object} formResponses - all answers in the store + */ +export const navigateForward = (SHORT_NAME, formResponses, router) => { + const roadmap = makeRoadmap(); + + if (roadmap?.length) { + const CURRENT_INDEX = roadmap?.indexOf(SHORT_NAME); + const END_INDEX = roadmap?.length - 1; + let nextIndex = CURRENT_INDEX + 1; + + if (CURRENT_INDEX === END_INDEX) { + // Results logic + + return; + } + + while (CURRENT_INDEX !== END_INDEX) { + const nextShortName = roadmap?.[nextIndex]; + + if (nextIndex > END_INDEX) { + // No questions after this one in the flow have their display conditions met + // Most likely a results page should show + + // Results logic + return; + } + + if (DISPLAY_CONDITIONS?.[nextShortName]) { + // Found entry in DISPLAY_CONDITIONS for next question + if (displayConditionsMet(nextShortName, formResponses)) { + pushToRoute(nextShortName, router); + return; + } + + nextIndex += 1; + } else { + // No entry in DISPLAY_CONDITIONS for next question + printErrorMessage('Unable to determine next question to display'); + return; + } + } + } else { + printErrorMessage('Unable to determine flow'); + } +}; diff --git a/src/applications/discharge-wizard/utilities/page-setup.js b/src/applications/discharge-wizard/utilities/page-setup.js index 95fbba878a8d..83dde57d7537 100644 --- a/src/applications/discharge-wizard/utilities/page-setup.js +++ b/src/applications/discharge-wizard/utilities/page-setup.js @@ -53,7 +53,6 @@ export const pageSetup = H1 => { export const applyErrorFocus = id => { const element = document.getElementById(id); - // focusElement(element) const tabindex = element.getAttribute('tabindex'); if (element.tabIndex !== 0) { element.setAttribute('tabindex', '-1');