diff --git a/src/applications/disability-benefits/all-claims/config/form0781/index.js b/src/applications/disability-benefits/all-claims/config/form0781/index.js index 76c3a4ea9d88..81c0d60137af 100644 --- a/src/applications/disability-benefits/all-claims/config/form0781/index.js +++ b/src/applications/disability-benefits/all-claims/config/form0781/index.js @@ -2,13 +2,20 @@ import * as workflowChoicePage from '../../pages/form0781/workflowChoicePage'; import * as mentalHealthSupport from '../../pages/form0781/mentalHealthSupport'; import * as traumaticEventsIntro from '../../pages/form0781/traumaticEventsIntro'; import * as eventType from '../../pages/form0781/traumaticEventTypes'; +import * as consentPage from '../../pages/form0781/consentPage'; +import * as additionalInformationPage from '../../pages/form0781/additionalInformationPage'; +import * as behaviorIntroPage from '../../pages/form0781/behaviorIntroPage'; +import * as behaviorIntroCombatPage from '../../pages/form0781/behaviorIntroCombatPage'; +import * as behaviorListPage from '../../pages/form0781/behaviorListPage'; + import { - showForm0781Pages, isCompletingForm0781, + showForm0781Pages, isRelatedToMST, + showBehaviorIntroPage, + showBehaviorIntroCombatPage, + showBehaviorListPage, } from '../../utils/form0781'; -import * as consentPage from '../../pages/form0781/consentPage'; -import * as additionalInformationPage from '../../pages/form0781/additionalInformationPage'; /** * Configuration for our modern 0781 paper sync (2024/2025) @@ -43,16 +50,35 @@ export const form0781PagesConfig = { uiSchema: eventType.uiSchema, schema: eventType.schema, }, - consentPage: { - path: 'additional-forms/mental-health-statement/consent', - depends: formData => isRelatedToMST(formData), - uiSchema: consentPage.uiSchema, - schema: consentPage.schema, - }, additionalInformationPage: { path: 'additional-forms/mental-health-statement/additional-information', depends: formData => isCompletingForm0781(formData), uiSchema: additionalInformationPage.uiSchema, schema: additionalInformationPage.schema, }, + // Behavioral Changes Pages + behaviorIntroPage: { + path: 'additional-forms/mental-health-statement/behavior-changes', + depends: formData => showBehaviorIntroPage(formData), + uiSchema: behaviorIntroPage.uiSchema, + schema: behaviorIntroPage.schema, + }, + behaviorIntroCombatPage: { + path: 'additional-forms/mental-health-statement/behavior-changes-combat', + depends: formData => showBehaviorIntroCombatPage(formData), + uiSchema: behaviorIntroCombatPage.uiSchema, + schema: behaviorIntroCombatPage.schema, + }, + behaviorListPage: { + path: 'additional-forms/mental-health-statement/behavior-changes-list', + depends: formData => showBehaviorListPage(formData), + uiSchema: behaviorListPage.uiSchema, + schema: behaviorListPage.schema, + }, + consentPage: { + path: 'additional-forms/mental-health-statement/consent', + depends: formData => isRelatedToMST(formData), + uiSchema: consentPage.uiSchema, + schema: consentPage.schema, + }, }; diff --git a/src/applications/disability-benefits/all-claims/constants.js b/src/applications/disability-benefits/all-claims/constants.js index 987f6c61ccda..9c9c713ef6ea 100644 --- a/src/applications/disability-benefits/all-claims/constants.js +++ b/src/applications/disability-benefits/all-claims/constants.js @@ -411,3 +411,29 @@ export const TRAUMATIC_EVENT_TYPES = Object.freeze({ nonMst: 'Traumatic events related to other personal interactions', other: 'Other traumatic events', }); + +export const BEHAVIOR_CHANGES_WORK = Object.freeze({ + reassignment: + 'Request for a change in occupational series or duty assignment', + absences: 'Increased or decreased use of leave', + performance: 'Changes in performance or performance evaluations', +}); + +export const BEHAVIOR_CHANGES_HEALTH = Object.freeze({ + consultations: + 'Increased or decreased visits to a healthcare professional, counselor, or treatment facility', + episodes: 'Episodes of depression, panic attacks, or anxiety', + medications: 'Increased or decreased use of prescription medications', + selfMedication: 'Increased or decreased use of over-the-counter medications', + substances: 'Increased or decreased use of alcohol or drugs', + appetite: + 'Changes in eating habits, such as overeating or undereating, or significant changes in weight', + pregnancy: 'Pregnancy tests around the time of the traumatic experiences', + screenings: 'Tests for sexually transmitted infections', +}); + +export const BEHAVIOR_CHANGES_OTHER = Object.freeze({ + socialEconomic: 'Economic or social behavioral changes', + relationships: 'Changes in or breakup of a significant relationship', + misconduct: 'Disciplinary or legal difficulties', +}); diff --git a/src/applications/disability-benefits/all-claims/content/form0781/behaviorListPages.jsx b/src/applications/disability-benefits/all-claims/content/form0781/behaviorListPages.jsx new file mode 100644 index 000000000000..b2ebcd32b7bb --- /dev/null +++ b/src/applications/disability-benefits/all-claims/content/form0781/behaviorListPages.jsx @@ -0,0 +1,16 @@ +import React from 'react'; + +export const BEHAVIOR_LIST_DESCRIPTION = ( +
+ Did you experience any of these behavioral changes after your traumatic + experiences? It’s also okay if you don’t report any behavioral changes. You + can skip this question if you don’t feel comfortable answering. +
+); + +export const BEHAVIOR_LIST_BEHAVIORS_TITLE = + 'Behavioral changes related to work'; + +export const BEHAVIOR_INTRO_COMBAT_DESCRIPTION = ( +Placholder content for combat intro description
+); diff --git a/src/applications/disability-benefits/all-claims/pages/form0781/behaviorIntroCombatPage.js b/src/applications/disability-benefits/all-claims/pages/form0781/behaviorIntroCombatPage.js new file mode 100644 index 000000000000..e9231ecfc5da --- /dev/null +++ b/src/applications/disability-benefits/all-claims/pages/form0781/behaviorIntroCombatPage.js @@ -0,0 +1,27 @@ +import { + radioUI, + radioSchema, +} from 'platform/forms-system/src/js/web-component-patterns'; + +import { BEHAVIOR_INTRO_COMBAT_DESCRIPTION } from '../../content/form0781/behaviorListPages'; + +export const uiSchema = { + 'ui:description': BEHAVIOR_INTRO_COMBAT_DESCRIPTION, + 'view:answerCombatBehaviorQuestions': { + ...radioUI({ + title: 'Do you want to answer additional questions?', + required: () => true, + labels: { + true: 'true', + false: 'false', + }, + }), + }, +}; + +export const schema = { + type: 'object', + properties: { + 'view:answerCombatBehaviorQuestions': radioSchema(['true', 'false']), + }, +}; diff --git a/src/applications/disability-benefits/all-claims/pages/form0781/behaviorIntroPage.js b/src/applications/disability-benefits/all-claims/pages/form0781/behaviorIntroPage.js new file mode 100644 index 000000000000..3a065fa325c7 --- /dev/null +++ b/src/applications/disability-benefits/all-claims/pages/form0781/behaviorIntroPage.js @@ -0,0 +1,8 @@ +export const uiSchema = { + 'ui:description': 'Placeholder Text for Behavior Intro', +}; + +export const schema = { + type: 'object', + properties: {}, +}; diff --git a/src/applications/disability-benefits/all-claims/pages/form0781/behaviorListPage.js b/src/applications/disability-benefits/all-claims/pages/form0781/behaviorListPage.js new file mode 100644 index 000000000000..cfa34bc0c534 --- /dev/null +++ b/src/applications/disability-benefits/all-claims/pages/form0781/behaviorListPage.js @@ -0,0 +1,79 @@ +import { + checkboxGroupSchema, + checkboxGroupUI, +} from 'platform/forms-system/src/js/web-component-patterns'; +import { + BEHAVIOR_LIST_DESCRIPTION, + BEHAVIOR_LIST_BEHAVIORS_TITLE, +} from '../../content/form0781/behaviorListPages'; +import { + BEHAVIOR_CHANGES_WORK, + BEHAVIOR_CHANGES_HEALTH, + BEHAVIOR_CHANGES_OTHER, +} from '../../constants'; + +const schemaKeys = Object.keys(BEHAVIOR_CHANGES_WORK).concat( + Object.keys(BEHAVIOR_CHANGES_HEALTH), + Object.keys(BEHAVIOR_CHANGES_OTHER), +); + +export const uiSchema = { + 'ui:description': BEHAVIOR_LIST_DESCRIPTION, + behaviors: checkboxGroupUI({ + title: BEHAVIOR_LIST_BEHAVIORS_TITLE, + labels: { + ...BEHAVIOR_CHANGES_WORK, + ...BEHAVIOR_CHANGES_HEALTH, + ...BEHAVIOR_CHANGES_OTHER, + }, + required: false, + }), + otherBehaviors: { + 'ui:title': 'placeholder title', + 'ui:description': 'placeholde description', + }, + 'view:optOut': checkboxGroupUI({ + title: 'None', + labels: { + none: 'no selection placeholder', + }, + required: false, + }), + 'ui:validations': [ + (errors, field) => { + const behaviorSelected = Object.values(field.behaviors || {}).some( + selected => selected, + ); + const otherProvided = Object.values(field.otherBehaviors || {}).some( + entry => !!entry, + ); + const optedOut = !!Object.values(field['view:optOut'] || {}).some( + entry => !!entry, + ); + + if (!behaviorSelected && !otherProvided && !optedOut) { + // when a user has not selected options nor opted out + errors['view:optOut'].addError( + 'selection required Error message placehoder', + ); + } else if (optedOut && (behaviorSelected || otherProvided)) { + // when a user has selected options and opted out + errors['view:optOut'].addError( + 'conflicting selections Error message placehoder', + ); + } + }, + ], +}; + +export const schema = { + type: 'object', + properties: { + behaviors: checkboxGroupSchema(schemaKeys), + otherBehaviors: { + type: 'string', + properties: {}, + }, + 'view:optOut': checkboxGroupSchema(['none']), + }, +}; diff --git a/src/applications/disability-benefits/all-claims/tests/pages/form0781/behaviorIntroCombatPage.unit.spec.js b/src/applications/disability-benefits/all-claims/tests/pages/form0781/behaviorIntroCombatPage.unit.spec.js new file mode 100644 index 000000000000..e9dcf736f6de --- /dev/null +++ b/src/applications/disability-benefits/all-claims/tests/pages/form0781/behaviorIntroCombatPage.unit.spec.js @@ -0,0 +1,12 @@ +import { expect } from 'chai'; +import * as behaviorIntroCombatPage from '../../../pages/form0781/behaviorIntroCombatPage'; + +describe('Behavior Intro Page when Combat is the only type selected', () => { + it('should define a uiSchema object', () => { + expect(behaviorIntroCombatPage.uiSchema).to.be.an('object'); + }); + + it('should define a schema object', () => { + expect(behaviorIntroCombatPage.schema).to.be.an('object'); + }); +}); diff --git a/src/applications/disability-benefits/all-claims/tests/pages/form0781/behaviorIntroPage.unit.spec.js b/src/applications/disability-benefits/all-claims/tests/pages/form0781/behaviorIntroPage.unit.spec.js new file mode 100644 index 000000000000..2cbc19226ee2 --- /dev/null +++ b/src/applications/disability-benefits/all-claims/tests/pages/form0781/behaviorIntroPage.unit.spec.js @@ -0,0 +1,12 @@ +import { expect } from 'chai'; +import * as behaviorIntroPage from '../../../pages/form0781/behaviorIntroPage'; + +describe('Behavior Intro Page', () => { + it('should define a uiSchema object', () => { + expect(behaviorIntroPage.uiSchema).to.be.an('object'); + }); + + it('should define a schema object', () => { + expect(behaviorIntroPage.schema).to.be.an('object'); + }); +}); diff --git a/src/applications/disability-benefits/all-claims/tests/pages/form0781/behaviorListPage.unit.spec.js b/src/applications/disability-benefits/all-claims/tests/pages/form0781/behaviorListPage.unit.spec.js new file mode 100644 index 000000000000..3150bfc4757f --- /dev/null +++ b/src/applications/disability-benefits/all-claims/tests/pages/form0781/behaviorListPage.unit.spec.js @@ -0,0 +1,12 @@ +import { expect } from 'chai'; +import * as behaviorListPage from '../../../pages/form0781/behaviorListPage'; + +describe('Behavior List Page', () => { + it('should define a uiSchema object', () => { + expect(behaviorListPage.uiSchema).to.be.an('object'); + }); + + it('should define a schema object', () => { + expect(behaviorListPage.schema).to.be.an('object'); + }); +}); diff --git a/src/applications/disability-benefits/all-claims/tests/utils/form0781.unit.spec.js b/src/applications/disability-benefits/all-claims/tests/utils/form0781.unit.spec.js new file mode 100644 index 000000000000..e2983f71d6df --- /dev/null +++ b/src/applications/disability-benefits/all-claims/tests/utils/form0781.unit.spec.js @@ -0,0 +1,219 @@ +import { expect } from 'chai'; + +import { + showForm0781Pages, + showBehaviorIntroPage, + showBehaviorIntroCombatPage, + showBehaviorListPage, +} from '../../utils/form0781'; + +describe('showForm0781Pages', () => { + describe('when the flipper is on and a user is claiming a new condition', () => { + it('should should return true', () => { + const formData = { + syncModern0781Flow: true, + mentalHealth: { + conditions: { + someCondition: true, + }, + }, + }; + expect(showForm0781Pages(formData)).to.eq(true); + }); + }); + + describe('when the flipper is off and a user is claiming a new condition', () => { + it('should should return false', () => { + const formData = { + syncModern0781Flow: false, + mentalHealth: { + conditions: { + someCondition: true, + }, + }, + }; + expect(showForm0781Pages(formData)).to.eq(false); + }); + }); + + describe('when the flipper is on and a user is not claiming a new condition', () => { + it('should should return false', () => { + const formData = { + syncModern0781Flow: true, + mentalHealth: { + conditions: { + none: true, + }, + }, + }; + expect(showForm0781Pages(formData)).to.eq(false); + }); + + it('should should return false', () => { + const formData = { + syncModern0781Flow: true, + mentalHealth: { + conditions: { + someCondition: false, + }, + }, + }; + expect(showForm0781Pages(formData)).to.eq(false); + }); + }); +}); + +describe('showBehaviorIntroCombatPage', () => { + describe('when a user has selected ONLY combat related events', () => { + it('should should return true', () => { + const formData = { + syncModern0781Flow: true, + mentalHealth: { + conditions: { + someCondition: true, + }, + eventTypes: { + combat: true, + nonMst: false, + }, + }, + }; + + expect(showBehaviorIntroCombatPage(formData)).to.eq(true); + }); + }); + + describe('when a user has selected combat related AND non-combat related events', () => { + it('should should return false', () => { + const formData = { + syncModern0781Flow: true, + mentalHealth: { + conditions: { + someCondition: true, + }, + eventTypes: { + combat: true, + nonMst: true, + }, + }, + }; + + expect(showBehaviorIntroCombatPage(formData)).to.eq(false); + }); + }); + + describe('when a user has not selected combat related events', () => { + it('should should return false', () => { + const formData = { + syncModern0781Flow: true, + mentalHealth: { + conditions: { + someCondition: true, + }, + eventTypes: { + combat: false, + nonMst: true, + }, + }, + }; + + expect(showBehaviorIntroCombatPage(formData)).to.eq(false); + }); + }); +}); + +describe('showBehaviorIntroPage', () => { + describe('when a user has not selected ONLY combat related events', () => { + it('should should return true', () => { + const formData = { + syncModern0781Flow: true, + mentalHealth: { + conditions: { + someCondition: true, + }, + eventTypes: { + combat: true, + nonMst: true, + }, + }, + }; + expect(showBehaviorIntroPage(formData)).to.eq(true); + }); + }); + + describe('when a user has selected ONLY combat related events', () => { + it('should should return false', () => { + const formData = { + syncModern0781Flow: true, + mentalHealth: { + conditions: { + someCondition: true, + }, + eventTypes: { + combat: true, + nonMst: false, + }, + }, + }; + expect(showBehaviorIntroPage(formData)).to.eq(false); + }); + }); +}); + +describe('showBehaviorListPage', () => { + describe('when a user has selected ONLY combat related events and opted in', () => { + it('should should return true', () => { + const formData = { + syncModern0781Flow: true, + 'view:answerCombatBehaviorQuestions': 'true', + mentalHealth: { + conditions: { + someCondition: true, + }, + eventTypes: { + combat: true, + nonMst: false, + }, + }, + }; + expect(showBehaviorListPage(formData)).to.eq(true); + }); + }); + + describe('when a user has selected ONLY combat related events and opted OUT', () => { + it('should should return false', () => { + const formData = { + syncModern0781Flow: true, + 'view:answerCombatBehaviorQuestions': 'false', + mentalHealth: { + conditions: { + someCondition: true, + }, + eventTypes: { + combat: true, + nonMst: false, + }, + }, + }; + expect(showBehaviorListPage(formData)).to.eq(false); + }); + }); + + describe('when a user has not selected ONLY combat related events', () => { + it('should should return true', () => { + const formData = { + syncModern0781Flow: true, + mentalHealth: { + conditions: { + someCondition: true, + }, + eventTypes: { + combat: true, + nonMst: true, + }, + }, + }; + expect(showBehaviorListPage(formData)).to.eq(true); + }); + }); +}); diff --git a/src/applications/disability-benefits/all-claims/utils/form0781.js b/src/applications/disability-benefits/all-claims/utils/form0781.js index c8c178416e92..b3f5bf7c0a22 100644 --- a/src/applications/disability-benefits/all-claims/utils/form0781.js +++ b/src/applications/disability-benefits/all-claims/utils/form0781.js @@ -1,7 +1,22 @@ // All flippers for the 0781 Papersync should be added to this file +import _ from 'platform/utilities/data'; import { isClaimingNew } from '.'; import { form0781WorkflowChoices } from '../content/form0781'; +/** + * Helper method to determin if a series of veteran selections match ONLY + * the condition of type === combat + */ +function combatOnlySelection(formData) { + const eventTypes = formData?.mentalHealth?.eventTypes || {}; + // ensure the Vet has only selected the 'combat' event type + const combatSelected = eventTypes.combat; + const nonCombatSelections = Object.keys(eventTypes) + .filter(key => key !== 'combat') + .some(key => eventTypes[key]); + return combatSelected && !nonCombatSelections; +} + /** * Checks if the modern 0781 flow should be shown if the flipper is active for this veteran * All 0781 page-specific flippers should include a check against this top level flipper @@ -56,3 +71,45 @@ export function isRelatedToMST(formData) { formData?.mentalHealth?.eventTypes?.mst === true ); } +/* + * @returns + * TRUE + * - IF Vetern should see 0781 pages + * - AND is not seeing the 'Combat Only' version of this page + */ +export function showBehaviorIntroPage(formData) { + return showForm0781Pages(formData) && !combatOnlySelection(formData); +} + +/* + * @returns + * FALSE + * - IF Vetern has ONLY selected "Traumatic Events Related To Comabt" + * - AND has explicitly opted out of providing more info + * - ELSE IF Veteran should not see 0781 pages + * TRUE + * - in all other cases + */ +export function showBehaviorIntroCombatPage(formData) { + return showForm0781Pages(formData) && combatOnlySelection(formData); +} + +/* + * @returns + * FALSE + * - IF Vetern has ONLY selected "Traumatic Events Related To Comabt" + * - AND has explicitly opted out of providing more info + * - ELSE IF Veteran should not see 0781 pages + * TRUE + * - in all other cases + */ +export function showBehaviorListPage(formData) { + const answerQuestions = + _.get('view:answerCombatBehaviorQuestions', formData, 'false') === 'true'; + + return ( + showForm0781Pages(formData) && + ((showBehaviorIntroCombatPage(formData) && answerQuestions) || + !combatOnlySelection(formData)) + ); +}