-
Notifications
You must be signed in to change notification settings - Fork 128
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Travel Pay / add content + functionality to question pages #33979
base: main
Are you sure you want to change the base?
Changes from 62 commits
5c33296
f8958e3
88307e5
781ef9e
00c1b51
b4de3cd
0382e89
b3a8982
76e0a0d
e483314
3354a49
14aeb9f
30eb709
5903efe
ce5c199
d6abddd
335feaf
542f1a2
4c97164
f7c3625
9bb8a9a
14d26e0
5e91b29
9b56d6c
e3e23a0
378adcc
2e43932
cc1c958
87c5d8e
a6d39be
cfcc321
184357d
1c4a954
0d2d739
3d60796
e762dc5
1f690d2
59a7e0f
369bc97
01603d8
d1376ff
cc2fb97
f4e62ce
4ff674d
26d9cf9
3ae445d
3f5bb3b
5e18d4d
b61565d
0ea2a53
fbb0d79
c4d8549
5d4aeaa
4391645
afc7f26
6417974
b5d4bc6
2b23be7
fd0c994
3c49f6f
4017d92
c389495
daccdc8
f182c71
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,28 +1,174 @@ | ||
import React from 'react'; | ||
import React, { useState, useEffect } from 'react'; | ||
import PropTypes from 'prop-types'; | ||
|
||
import { VaButtonPair } from '@department-of-veterans-affairs/component-library/dist/react-bindings'; | ||
import { | ||
VaButtonPair, | ||
VaRadio, | ||
} from '@department-of-veterans-affairs/component-library/dist/react-bindings'; | ||
import { focusElement, scrollToTop } from 'platform/utilities/ui'; | ||
|
||
import { HelpTextGeneral, HelpTextModalities } from '../../HelpText'; | ||
|
||
const AddressPage = ({ | ||
address, | ||
pageIndex, | ||
setPageIndex, | ||
yesNo, | ||
setYesNo, | ||
setIsUnsupportedClaimType, | ||
}) => { | ||
useEffect( | ||
() => { | ||
scrollToTop('topScrollElement'); | ||
if (!address) { | ||
focusElement('h1'); | ||
} else { | ||
focusElement('h1', {}, 'va-radio'); | ||
} | ||
}, | ||
[address], | ||
); | ||
|
||
const [requiredAlert, setRequiredAlert] = useState(false); | ||
|
||
const handlers = { | ||
onNext: () => { | ||
if (!yesNo.address) { | ||
setRequiredAlert(true); | ||
} else if (yesNo.address !== 'yes') { | ||
setIsUnsupportedClaimType(true); | ||
} else { | ||
setIsUnsupportedClaimType(false); | ||
setPageIndex(pageIndex + 1); | ||
} | ||
}, | ||
onBack: () => { | ||
setPageIndex(pageIndex - 1); | ||
}, | ||
}; | ||
|
||
if (!address) { | ||
return ( | ||
<> | ||
<h1 className="vads-u-margin-bottom--2"> | ||
Did you travel from your home address? | ||
</h1> | ||
<va-alert | ||
close-btn-aria-label="Close notification" | ||
status="warning" | ||
visible | ||
> | ||
<h2 slot="headline">You don’t have an address on file</h2> | ||
<p className="vads-u-margin-y--0"> | ||
We’re sorry, we don’t have an address on file for you and can’t file | ||
a claim in this tool right now. | ||
</p> | ||
</va-alert> | ||
<HelpTextModalities /> | ||
<HelpTextGeneral /> | ||
<va-button back onClick={handlers.onBack} class="vads-u-margin-y--2" /> | ||
</> | ||
); | ||
} | ||
|
||
const AddressPage = ({ handlers }) => { | ||
return ( | ||
<div> | ||
<h1>Address page</h1> | ||
<VaRadio | ||
use-forms-pattern="single" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we standardize around either the react bindings or the web components rather than a mix of both? Or is there a reason to use one vs. the other considering the scenario? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We could standardize throughout our app if we decide to. Not every web component has a corresponding react binding (I don't think - for example the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh okay, understood. I don't think I have an opinion about one vs. the other, but was curious if there was something I wasn't aware of driving the choice to use either. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Honestly now I'm second guessing myself... 🤣 For example, based on the Storybook there isn't a react binding for |
||
form-heading="Did you travel from your home address?" | ||
form-heading-level={1} | ||
id="address" | ||
onVaValueChange={e => { | ||
setYesNo({ ...yesNo, address: e.detail.value }); | ||
}} | ||
value={yesNo.address} | ||
data-testid="address-test-id" | ||
error={requiredAlert ? 'You must make a selection to continue.' : null} | ||
header-aria-describedby={null} | ||
hint="" | ||
label="" | ||
label-header-level="" | ||
> | ||
<div slot="form-description"> | ||
<p> | ||
Answer “Yes” if you traveled from the address listed here and you | ||
confirm that it’s not a Post Office box. | ||
</p> | ||
<hr className="vads-u-margin-y--0" /> | ||
<p className="vads-u-margin-top--2"> | ||
<strong>Home address</strong> | ||
<br /> | ||
{address.addressLine1} | ||
<br /> | ||
{address.addressLine2 && ( | ||
<> | ||
{address.addressLine2} | ||
<br /> | ||
</> | ||
)} | ||
{address.addressLine3 && ( | ||
<> | ||
{address.addressLine3} | ||
<br /> | ||
</> | ||
)} | ||
{`${address.city}, ${address.stateCode} ${address.zipCode}`} | ||
<br /> | ||
</p> | ||
<hr className="vads-u-margin-y--0" /> | ||
</div> | ||
<va-radio-option | ||
label="Yes" | ||
value="yes" | ||
key="address-yes" | ||
name="address" | ||
checked={yesNo.address === 'yes'} | ||
/> | ||
<va-radio-option | ||
key="address-no" | ||
name="address" | ||
checked={yesNo.address === 'no'} | ||
label="No" | ||
value="no" | ||
/> | ||
</VaRadio> | ||
|
||
<va-additional-info | ||
class="vads-u-margin-y--3" | ||
trigger="If you didn't travel from your home address" | ||
> | ||
<p> | ||
<strong> | ||
If you traveled from a different address, you can’t file a claim in | ||
this tool right now. | ||
</strong>{' '} | ||
But you can file your claim online, within 30 days, through the | ||
<va-link | ||
external | ||
href="https://link-to-btsss" | ||
text="Beneficiary Travel Self Service System (BTSSS)" | ||
/> | ||
. Or you can use VA Form 10-3542 to submit a claim by mail or in | ||
person. | ||
</p> | ||
</va-additional-info> | ||
<VaButtonPair | ||
class="vads-u-margin-y--2" | ||
continue | ||
onPrimaryClick={e => handlers.onNext(e)} | ||
onSecondaryClick={e => handlers.onBack(e)} | ||
onPrimaryClick={handlers.onNext} | ||
onSecondaryClick={handlers.onBack} | ||
/> | ||
</div> | ||
); | ||
}; | ||
|
||
AddressPage.propTypes = { | ||
handlers: PropTypes.shape({ | ||
onBack: PropTypes.func, | ||
onNext: PropTypes.func, | ||
}), | ||
address: PropTypes.object, | ||
pageIndex: PropTypes.number, | ||
setIsUnsupportedClaimType: PropTypes.func, | ||
setPageIndex: PropTypes.func, | ||
setYesNo: PropTypes.func, | ||
yesNo: PropTypes.object, | ||
}; | ||
|
||
export default AddressPage; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,28 +1,149 @@ | ||
import React from 'react'; | ||
import React, { useState, useEffect } from 'react'; | ||
import PropTypes from 'prop-types'; | ||
|
||
import { VaButtonPair } from '@department-of-veterans-affairs/component-library/dist/react-bindings'; | ||
import { | ||
VaButtonPair, | ||
VaRadio, | ||
} from '@department-of-veterans-affairs/component-library/dist/react-bindings'; | ||
import { focusElement, scrollToTop } from 'platform/utilities/ui'; | ||
|
||
const MileagePage = ({ handlers }) => { | ||
import { formatDateTime } from '../../../util/dates'; | ||
|
||
const MileagePage = ({ | ||
appointment, | ||
pageIndex, | ||
setPageIndex, | ||
yesNo, | ||
setYesNo, | ||
setIsUnsupportedClaimType, | ||
}) => { | ||
useEffect(() => { | ||
focusElement('h1', {}, 'va-radio'); | ||
scrollToTop('topScrollElement'); | ||
}, []); | ||
|
||
const [formattedDate, formattedTime] = formatDateTime( | ||
appointment.vaos.apiData.start, | ||
); | ||
|
||
const [requiredAlert, setRequiredAlert] = useState(false); | ||
|
||
const handlers = { | ||
onNext: () => { | ||
if (!yesNo.mileage) { | ||
setRequiredAlert(true); | ||
} else if (yesNo.mileage !== 'yes') { | ||
setIsUnsupportedClaimType(true); | ||
} else { | ||
setIsUnsupportedClaimType(false); | ||
setPageIndex(pageIndex + 1); | ||
} | ||
}, | ||
onBack: () => { | ||
setPageIndex(pageIndex - 1); | ||
}, | ||
}; | ||
return ( | ||
<div> | ||
<h1>Mileage page</h1> | ||
<VaRadio | ||
use-forms-pattern="single" | ||
form-heading="Are you claiming only mileage?" | ||
form-heading-level={1} | ||
id="mileage" | ||
onVaValueChange={e => { | ||
setYesNo({ ...yesNo, mileage: e.detail.value }); | ||
}} | ||
value={yesNo.mileage} | ||
data-testid="mileage-test-id" | ||
error={requiredAlert ? 'You must make a selection to continue.' : null} | ||
header-aria-describedby={null} | ||
hint="" | ||
label="" | ||
label-header-level="" | ||
> | ||
<div slot="form-description"> | ||
<hr className="vads-u-margin-y--0" /> | ||
<p> | ||
{' '} | ||
<strong> | ||
{formattedDate} {formattedTime} at{' '} | ||
{appointment.vaos.apiData.location.attributes.name} | ||
</strong> | ||
</p> | ||
<p>{appointment.vaos.apiData.reasonForAppointment}</p> | ||
<hr className="vads-u-margin-y--0" /> | ||
</div> | ||
<va-radio-option | ||
label="Yes" | ||
value="yes" | ||
key="mileage-yes" | ||
name="mileage" | ||
checked={yesNo.mileage === 'yes'} | ||
/> | ||
<va-radio-option | ||
key="mileage-no" | ||
name="mileage" | ||
checked={yesNo.mileage === 'no'} | ||
label="No" | ||
value="no" | ||
/> | ||
</VaRadio> | ||
|
||
<va-additional-info | ||
class="vads-u-margin-y--3" | ||
trigger="How do we calculate mileage" | ||
> | ||
<ul> | ||
<li>We pay round-trip mileage for your scheduled appointments.</li> | ||
<li> | ||
We may only pay return mileage for unscheduled appointments, like | ||
walk-ins and labs. | ||
</li> | ||
</ul> | ||
<va-link | ||
external | ||
href="https://www.va.gov/resources/reimbursed-va-travel-expenses-and-mileage-rate/#mileage-reimbursement-rate" | ||
text="Check current mileage rates" | ||
/> | ||
</va-additional-info> | ||
|
||
<va-additional-info | ||
class="vads-u-margin-bottom--3" | ||
trigger="If you have other expenses to claim" | ||
> | ||
<p> | ||
<strong> | ||
If you need to submit receipts for other expenses like tolls, meals, | ||
or lodging, you can’t file a claim in this tool right now. | ||
</strong>{' '} | ||
But you can file your claim online, within 30 days, through the{' '} | ||
<va-link | ||
external | ||
href="https://link-to-btsss" | ||
text="Beneficiary Travel Self Service System (BTSSS)" | ||
/> | ||
. Or you can use VA Form 10-3542 to submit a claim by mail or in | ||
person. | ||
</p> | ||
</va-additional-info> | ||
|
||
<VaButtonPair | ||
class="vads-u-margin-y--2" | ||
continue | ||
onPrimaryClick={e => handlers.onNext(e)} | ||
onSecondaryClick={e => handlers.onBack(e)} | ||
onPrimaryClick={handlers.onNext} | ||
onSecondaryClick={handlers.onBack} | ||
/> | ||
</div> | ||
); | ||
}; | ||
|
||
MileagePage.propTypes = { | ||
handlers: PropTypes.shape({ | ||
onBack: PropTypes.func, | ||
onNext: PropTypes.func, | ||
}), | ||
appointment: PropTypes.object, | ||
pageIndex: PropTypes.number, | ||
setIsUnsupportedClaimType: PropTypes.func, | ||
setPageIndex: PropTypes.func, | ||
setYesNo: PropTypes.func, | ||
yesNo: PropTypes.object, | ||
}; | ||
|
||
export default MileagePage; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the aim behind this to set the scroll height at a specific point or to make the screen reader call out the heading each time the view changes? I'm wondering if
aria-live
might help address the latter aim.I was under the impression that focusing non-interactive elements was something to be avoided.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a common pattern used in VA.gov. The
scrollToTop
moves the element into the window if on the previous page the user had scrolled down and thefocusElement
sends the screen reader to the correct starting point on the page (otherwise it starts at the top with the header and reads all the links/etc. in the header) to continue through the form.