diff --git a/docs/logical_data_model.md b/docs/logical_data_model.md index b8b2ec8cda..0cecb01951 100644 --- a/docs/logical_data_model.md +++ b/docs/logical_data_model.md @@ -1,14 +1,14 @@ Logical Data Model ================== -![logical data model diagram](http://www.plantuml.com/plantuml/png/nLTjRzis4FwkNy5lUm5Y31k60KLGDCtS8S2M1N7w0yxIHUP6yRZZAQsHvBzFD1FPjEI4jR3rIm8zz_xmZaU_ym8iRMCANu91zSFibv-BuXi5TwGhvPYM1XklFcbPAR2rmKgLJ9-ykaMKml-jhGE6HXbjKTOE5R6ig2XA0zwhZnvPtHTAVQywRs-Kje9r1vsUrcYHGR1w-tOZNddqGViYg7bXRUi4jz3WhYJWPsqNYhLez9q8c65z6XM2ptqBjoABbsspHDw5iG5jTW7HOl2DGjis8GurU8sr9dknKy2tF_tudRgUVvXVpo_FvmlbsgNHvImg-fckDhaRyF8xrTfsXg8Rz0ADsqXAnb3xcGmahQj698_FXCO8UPIc1FGk5YKvFCQ3KXNwahKJJRP79vLpXTnexxDBQZronZc4XkbFWOyyFHzuoZ3Ry1ciffxiFss7pPc9VGuRvCA79hjxGYthtSy6PKN9H74CyvwgTGfxHzRZ8VoSAkpnYOMI6Mk_brhsXfmkispICD4kgxEOJCvX1w_zMKjmVr6zPhF9Odg3_4PWyZXGoWj8wtUHuF2MzzIq_y3KB1fWyIj4kH6uCX0QQaba4EoaZEEnlPHstSaoI0yklXBfb_XP5e9kEv5f1L2AljEolaP7BA-dTPReFwTdIJ0PhW3Sh1sEJzp16iRxXi4APexS5B9Yf8Dn1flts8kvZWNbD4xK38Q3cOW8owyWk5Sescg8JYOcT776NNHCWIarkNR1wjWnd31HP6FMnxn1feLEXoydFAEdbilD4pHPer1LEknUC9Sac7HLRFG_Rw-0HPVQ1QQJMQZGw3lazH-3vLor_KQliDGDcAGRGt5zsoOGz9Lr0JLQ2PV7oDf7JgaSpeHZgXcQO3_sg1RW6kMAN6iscOxfC88dEo9m2IFxudYpZme4OZq3IKbMyBCa1K2D2udi_EN9Tb03jWCvFQcZtvC6l7oCZVP-_Hy_Hf_CfhVyjXcp-dwusDGHc-tBhgDeOllh8SDkUvTC9YfD_voCWIKn2F7_oDSNDMGm6Ky_VzLCJVk83VAKTgX0FJoSqBsAhHwHHpevURZV-LrOn9-hGpnQZGd8FEl75sYn-QiU7pPGoA68NTEhzVkv27y6Q7GvcwdmVyVbE1I3RhsbI0mSAkz3F9MIOYdFmSdX3etKlQmozDS_RL-DbTHoI1JftHCdvxmdIZyJdUI-Yfm5-RXuUmaSrdc7jceRyoy0) +logical data model diagram UML Source ---------- ``` @startuml -scale 0.75 +scale 0.70 ' avoid problems with angled crows feet skinparam linetype ortho @@ -170,6 +170,16 @@ class ActivityReport { * updatedAt : timestamp } +class Objective { + * id : integer <> + * goalId : integer(32) REFERENCES public.Goal.id + title : string, + ttaProvided : string, + status : string, + * createdAt : timestamp + * updatedAt : timestamp +} + class ActivityParticipant { * id : integer <> * activityReportId : integer(32) REFERENCES public.ActivityReport.id @@ -191,6 +201,14 @@ class ActivityReportGoal { * goalId : integer(32) REFERENCES public.Goal.id } +class ActivityReportObjective { + * id : integer <> + * activityReportId : integer(32) REFERENCES public.ActivityReport.id + * objectiveId : integer(32) REFERENCES public.Objective.id + * createdAt : timestamp + * updatedAt : timestamp +} + User ||-o{ Region User }o--|{ Permission Scope }o--|{ Permission @@ -211,6 +229,9 @@ ActivityReport .. NextSteps ActivityReport .. ActivityReportGoal Goal .. ActivityReportGoal Goal }|--|{ ActivityReport +Goal ||-o{ Objective +ActivityReportObjective }o--|{ Objective +ActivityReportObjective }o--|{ ActivityReport User ||-o{ ActivityReport ActivityReport ||-o{ ActivityParticipant @@ -222,7 +243,7 @@ NonGrantee ||-{ ActivityParticipant Instructions ------------ -1. [Edit this diagram with plantuml.com](http://www.plantuml.com/plantuml/uml/nLTjRzis4FwkNy5lUm5Y31k60KLGDCtS8S2M1N7w0yxIHUP6yRZZAQsHvBzFD1FPjEI4jR3rIm8zz_xmZaU_ym8iRMCANu91zSFibv-BuXi5TwGhvPYM1XklFcbPAR2rmKgLJ9-ykaMKml-jhGE6HXbjKTOE5R6ig2XA0zwhZnvPtHTAVQywRs-Kje9r1vsUrcYHGR1w-tOZNddqGViYg7bXRUi4jz3WhYJWPsqNYhLez9q8c65z6XM2ptqBjoABbsspHDw5iG5jTW7HOl2DGjis8GurU8sr9dknKy2tF_tudRgUVvXVpo_FvmlbsgNHvImg-fckDhaRyF8xrTfsXg8Rz0ADsqXAnb3xcGmahQj698_FXCO8UPIc1FGk5YKvFCQ3KXNwahKJJRP79vLpXTnexxDBQZronZc4XkbFWOyyFHzuoZ3Ry1ciffxiFss7pPc9VGuRvCA79hjxGYthtSy6PKN9H74CyvwgTGfxHzRZ8VoSAkpnYOMI6Mk_brhsXfmkispICD4kgxEOJCvX1w_zMKjmVr6zPhF9Odg3_4PWyZXGoWj8wtUHuF2MzzIq_y3KB1fWyIj4kH6uCX0QQaba4EoaZEEnlPHstSaoI0yklXBfb_XP5e9kEv5f1L2AljEolaP7BA-dTPReFwTdIJ0PhW3Sh1sEJzp16iRxXi4APexS5B9Yf8Dn1flts8kvZWNbD4xK38Q3cOW8owyWk5Sescg8JYOcT776NNHCWIarkNR1wjWnd31HP6FMnxn1feLEXoydFAEdbilD4pHPer1LEknUC9Sac7HLRFG_Rw-0HPVQ1QQJMQZGw3lazH-3vLor_KQliDGDcAGRGt5zsoOGz9Lr0JLQ2PV7oDf7JgaSpeHZgXcQO3_sg1RW6kMAN6iscOxfC88dEo9m2IFxudYpZme4OZq3IKbMyBCa1K2D2udi_EN9Tb03jWCvFQcZtvC6l7oCZVP-_Hy_Hf_CfhVyjXcp-dwusDGHc-tBhgDeOllh8SDkUvTC9YfD_voCWIKn2F7_oDSNDMGm6Ky_VzLCJVk83VAKTgX0FJoSqBsAhHwHHpevURZV-LrOn9-hGpnQZGd8FEl75sYn-QiU7pPGoA68NTEhzVkv27y6Q7GvcwdmVyVbE1I3RhsbI0mSAkz3F9MIOYdFmSdX3etKlQmozDS_RL-DbTHoI1JftHCdvxmdIZyJdUI-Yfm5-RXuUmaSrdc7jceRyoy0) +1. [Edit this diagram with plantuml.com](http://www.plantuml.com/plantuml/uml/nLTVRzis47_Nf-3R_g0nYkrXG14KJJDt2B2vHfn-05gwoEme7dSyfJ6IVFUP8aA8YfpGrfQ-9F3l_j_zZgG-E9R4RQCBLqeDuktijxT5yOEGDwWgOGdN6XedlYdU26bg3PKe2RyvSGt0XVjR6Ij9Da8h0xor891uWqLHQkcS-EA0n5qXnCz2LUATK8QXta6dfpKO8CbGlN_VYJSEdDU-y6gEIPawmOrie2_n-5cx2qMA5RYTI1B9xMbGy3w75dQ31XPjimRgmz18fVi0AzRmJe1f6ny76xY4Mg6vw1Nmuw-__2Gkvv_cb_F5-NmbRBlMgfm5HJTJLPUxbex_n5gPdI6h1XpBndQIai3NViPF9AsjHYGFZmqD4V9QcZOWkbYLQE4O3q9Ku4fIbXMQY4ugPmdKAEUoIseTKCOSF67Y5EZ8wJdVfmophf_UQkfa_sbts9m8oULq0wt_eT3q9zIIwlziW3UOR1I5C1-nsgQmisWSxCFFiOA8JomKzHZzdgFi5LfLvjl4CC4kYrc4EcVOtckSDNlt5zLBDbqMqmdo1qgTrmCX74jkNH_n3gzfeARS9y9crKXYjm7WAvPBBJsXreWQf9cKSHZRSsdNErb6qxsBxzJq1TpoYi6wM49QWInPtIZUNe95ufVJEalq7zCpo90ibmUkjXR6Dsvl3UEy8MaOybPkKhD2fBrnXZVlqGTp7GhA8KxGz8OZE-4Hujybmw1GCDK8EhKndiLvPz711QQjoRNpLWUluVIDGXEoFl8DCY5qkdmvuGaSjbHsJz3a6fLLfLYXaNg130ch0j4_NoybiIgLzPrETg42hMw0jdyzkPohDjxe5PIgaJfXeoykqZQ1PAni5r21ONzuZ5pxG4ahHtR24jOa6sy_zgXcIJNm4cshkqp7T0YaGpCYi6b71k9mieyAMFfyHYGb6ePRJWgGDQmuTEUhGxE36cbs8AMJElezgQNZBu5e7xV_ysDqyhPzNdy5pusCPJlm90YfiM8DiQTlmh2MX3UgWYh11rHxyz-iSc_9yYeSNjZYY1lxi6Csc9Xux6NOBQBoP0AawP-ZrdAD91dfjVfrwA9vaptfUzwzLjh7xCEUjECIlchk-1WrmqFCS0BV_Ep4_Vq9tebmU0Na0KzExkzwN_f5zy4yWWUhCG7ggD-zwulkhtYuxo3V_IBiIrIBBwx2_tc21fUpcV3_Uox7eH0qUXiW8R2lF8PuB2N1qYbv9_jtCxJkMPKHV_5ZvSLOIItxHA6z0t4GXJ57rXJx-1xwdg-OYXDE3KJfpqGdkHN2VBnyNDnxZkxL-G2cQXlzBm00) 2. Copy and paste the final UML into the UML Source section 3. Update the img src and edit link target to the current values diff --git a/frontend/package.json b/frontend/package.json index 901c15a1b1..094b4e2845 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -10,7 +10,7 @@ "@fortawesome/free-solid-svg-icons": "^5.15.1", "@fortawesome/react-fontawesome": "^0.1.11", "@hookform/error-message": "^0.0.5", - "@trussworks/react-uswds": "^1.12.2", + "@trussworks/react-uswds": "1.11.0", "@use-it/interval": "^1.0.0", "http-proxy-middleware": "^1.0.5", "lodash": "^4.17.20", diff --git a/frontend/src/App.css b/frontend/src/App.css index 29123b2c96..c7549c0f85 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -1,3 +1,7 @@ +.usa-skipnav:focus { + z-index: 999999; +} + .smart-hub-offset-nav { margin-left: 8rem; } diff --git a/frontend/src/components/DatePicker.css b/frontend/src/components/DatePicker.css index 1586359b58..d1fb65001e 100644 --- a/frontend/src/components/DatePicker.css +++ b/frontend/src/components/DatePicker.css @@ -7,7 +7,7 @@ with the tab key. Since the open button has tabindex of -1 we won't run into an issue of focus moving "backwards" */ - flex-direction: row-reverse; + /* flex-direction: row-reverse; */ width: fit-content; } diff --git a/frontend/src/components/DatePicker.js b/frontend/src/components/DatePicker.js index 3257e5d87e..cf080a8ce7 100644 --- a/frontend/src/components/DatePicker.js +++ b/frontend/src/components/DatePicker.js @@ -14,7 +14,7 @@ import React, { useState } from 'react'; import PropTypes from 'prop-types'; import { SingleDatePicker } from 'react-dates'; import { OPEN_UP, OPEN_DOWN } from 'react-dates/constants'; -import { Controller } from 'react-hook-form'; +import { Controller } from 'react-hook-form/dist/index.ie11'; import moment from 'moment'; import './DatePicker.css'; @@ -22,7 +22,7 @@ import './DatePicker.css'; const dateFmt = 'MM/DD/YYYY'; const DateInput = ({ - control, minDate, name, disabled, maxDate, openUp, required, + control, minDate, name, disabled, maxDate, openUp, required, ariaName, }) => { const hintId = `${name}-hint`; const [isFocused, updateFocus] = useState(false); @@ -35,6 +35,8 @@ const DateInput = ({ return isBefore || isAfter; }; + const message = isFocused ? '' : 'Navigate forward and push button to open the calendar'; + return ( <>
mm/dd/yyyy
@@ -43,22 +45,35 @@ const DateInput = ({ const date = value ? moment(value, dateFmt) : null; return (
-
); @@ -80,6 +95,7 @@ DateInput.propTypes = { // eslint-disable-next-line react/forbid-prop-types control: PropTypes.object.isRequired, name: PropTypes.string.isRequired, + ariaName: PropTypes.string.isRequired, minDate: PropTypes.string, maxDate: PropTypes.string, openUp: PropTypes.bool, diff --git a/frontend/src/components/FormItem.js b/frontend/src/components/FormItem.js index 70d6635de4..1a44644c5a 100644 --- a/frontend/src/components/FormItem.js +++ b/frontend/src/components/FormItem.js @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { useFormContext } from 'react-hook-form'; +import { useFormContext } from 'react-hook-form/dist/index.ie11'; import { ErrorMessage as ReactHookFormError } from '@hookform/error-message'; import { Label, FormGroup, ErrorMessage, Fieldset, diff --git a/frontend/src/components/MultiSelect.js b/frontend/src/components/MultiSelect.js index fee1bef4ba..73dfd2a45e 100644 --- a/frontend/src/components/MultiSelect.js +++ b/frontend/src/components/MultiSelect.js @@ -23,7 +23,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import Select, { components } from 'react-select'; -import { Controller } from 'react-hook-form'; +import { Controller } from 'react-hook-form/dist/index.ie11'; import arrowBoth from '../images/arrow-both.svg'; @@ -34,43 +34,6 @@ const DropdownIndicator = (props) => ( ); -const styles = { - container: (provided, state) => { - // To match the focus indicator provided by uswds - const outline = state.isFocused ? '0.25rem solid #2491ff;' : ''; - return { - ...provided, - outline, - }; - }, - groupHeading: (provided) => ({ - ...provided, - fontWeight: 'bold', - fontFamily: 'SourceSansPro', - textTransform: 'capitalize', - fontSize: '14px', - color: '#21272d', - lineHeight: '22px', - }), - control: (provided, state) => ({ - ...provided, - borderColor: '#565c65', - backgroundColor: 'white', - borderRadius: '0', - '&:hover': { - borderColor: '#565c65', - }, - // Match uswds disabled style - opacity: state.isDisabled ? '0.7' : '1', - }), - indicatorsContainer: (provided) => ({ - ...provided, - // The arrow dropdown icon is too far to the right, this pushes it back to the left - marginRight: '4px', - }), - indicatorSeparator: () => ({ display: 'none' }), -}; - function MultiSelect({ name, options, @@ -83,8 +46,47 @@ function MultiSelect({ rules, multiSelectOptions, onItemSelected, + singleRowInput, components: componentReplacements, }) { + const styles = { + container: (provided, state) => { + // To match the focus indicator provided by uswds + const outline = state.isFocused ? '0.25rem solid #2491ff;' : ''; + return { + ...provided, + outline, + }; + }, + groupHeading: (provided) => ({ + ...provided, + fontWeight: 'bold', + fontFamily: 'SourceSansPro', + textTransform: 'capitalize', + fontSize: '14px', + color: '#21272d', + lineHeight: '22px', + }), + control: (provided, state) => ({ + height: singleRowInput ? '38px' : '', + ...provided, + borderColor: '#565c65', + backgroundColor: 'white', + borderRadius: '0', + '&:hover': { + borderColor: '#565c65', + }, + // Match uswds disabled style + opacity: state.isDisabled ? '0.7' : '1', + }), + indicatorsContainer: (provided) => ({ + ...provided, + // The arrow dropdown icon is too far to the right, this pushes it back to the left + marginRight: '4px', + }), + indicatorSeparator: () => ({ display: 'none' }), + }; + /* * @param {Array || Array} - value array. Either an array of strings or array * of objects @@ -146,6 +148,7 @@ function MultiSelect({ components={{ ...componentReplacements, DropdownIndicator }} options={options} isDisabled={disabled} + tabSelectsValue={false} isClearable={multiSelectOptions.isClearable} closeMenuOnSelect={multiSelectOptions.closeMenuOnSelect} controlShouldRenderValue={multiSelectOptions.controlShouldRenderValue} @@ -196,6 +199,7 @@ MultiSelect.propTypes = { control: PropTypes.object.isRequired, components: PropTypes.shape({}), onItemSelected: PropTypes.func, + singleRowInput: PropTypes.bool, multiSelectOptions: PropTypes.shape({ isClearable: PropTypes.bool, closeMenuOnSelect: PropTypes.bool, @@ -209,6 +213,7 @@ MultiSelect.propTypes = { MultiSelect.defaultProps = { disabled: false, + singleRowInput: false, required: 'Please select at least one item', simple: true, labelProperty: 'label', diff --git a/frontend/src/components/Navigator/__tests__/index.js b/frontend/src/components/Navigator/__tests__/index.js index 658e3dfea3..bc0d4b3f42 100644 --- a/frontend/src/components/Navigator/__tests__/index.js +++ b/frontend/src/components/Navigator/__tests__/index.js @@ -5,7 +5,7 @@ import { render, screen, waitFor, within, } from '@testing-library/react'; -import { useFormContext } from 'react-hook-form'; +import { useFormContext } from 'react-hook-form/dist/index.ie11'; import Navigator from '../index'; import { NOT_STARTED } from '../constants'; @@ -91,7 +91,7 @@ describe('Navigator', () => { renderNavigator(); const firstInput = screen.getByTestId('first'); userEvent.click(firstInput); - const first = await screen.findByRole('button', { name: 'first page' }); + const first = await screen.findByRole('button', { name: 'first page In Progress' }); await waitFor(() => expect(within(first).getByText('In Progress')).toBeVisible()); }); @@ -121,7 +121,7 @@ describe('Navigator', () => { const updatePage = jest.fn(); const updateForm = jest.fn(); renderNavigator('second', () => {}, () => {}, updatePage, updateForm); - userEvent.click(await screen.findByRole('button', { name: 'first page' })); + userEvent.click(await screen.findByRole('button', { name: 'first page Not Started' })); await waitFor(() => expect(updateForm).toHaveBeenCalledWith({ ...initialData, second: null })); await waitFor(() => expect(updatePage).toHaveBeenCalledWith(1)); }); diff --git a/frontend/src/components/Navigator/components/SideNav.css b/frontend/src/components/Navigator/components/SideNav.css index d917223677..4d9b4881b6 100644 --- a/frontend/src/components/Navigator/components/SideNav.css +++ b/frontend/src/components/Navigator/components/SideNav.css @@ -1,8 +1,8 @@ .smart-hub--navigator-link-active { - background-color: #f8f8f8; - color: #0166ab; + background-color: rgb(248, 248, 248); + color: rgb(1, 102, 171); font-weight: bold; - border-left: 2px solid #0166ab; + border-left: 2px solid rgb(1, 102, 171); text-decoration-line: none; } @@ -13,45 +13,45 @@ width: 100%; height: 100%; font-size: 16px; - color: #12549d; + color: rgb(18, 84, 157); line-height: 24px; } .smart-hub--navigator-list { list-style-type: none; - padding-inline-start: 0; + padding-left: 0; margin: 0; } .smart-hub--navigator-item { width: 100%; height: 40px; - color: #0166ab; + color: rgb(1, 102, 171); } .smart-hub--tag-not-started { background-color: #eceef1; - color: #21272dc1; + color: rgba(33, 39, 45, 0.76); } .smart-hub--tag-in-progress { - background-color: #e2eff7; - color: #0166ab; + background-color: rgb(226, 239, 247); + color: rgb(1, 102, 171); } .smart-hub--tag-complete { - background-color: #e6faedb0; - color: #3a7e5a; + background-color: rgba(230, 250, 237, 0.69); + color: rgb(58, 126, 90); } .smart-hub--tag-submitted { - background-color: #0166ab; - color: #f8f8f8; + background-color: rgb(1, 102, 171); + color: rgb(248, 248, 248); } .smart-hub--tag-needs-action { - background-color: #f9e0e4; - color: #d42240; + background-color: rgb(249, 224, 228); + color: rgb(212, 34, 64); } .smart-hub--tag { @@ -67,7 +67,7 @@ font-family: Source Sans Pro Web, Helvetica Neue, Helvetica, Roboto, Arial, sans-serif; font-size: 1.06rem; line-height: 1.5; - color: #005ea2; + color: rgb(0, 94, 162); text-decoration: underline; background: transparent; left: 0; diff --git a/frontend/src/components/Navigator/components/SideNav.js b/frontend/src/components/Navigator/components/SideNav.js index c6afd88456..5fa6f267dd 100644 --- a/frontend/src/components/Navigator/components/SideNav.js +++ b/frontend/src/components/Navigator/components/SideNav.js @@ -48,7 +48,6 @@ function SideNav({ unstyled className={`smart-hub--navigator-link ${page.current ? 'smart-hub--navigator-link-active' : ''}`} role="button" - aria-label={page.label} > {page.label} @@ -79,8 +78,8 @@ function SideNav({ )} {lastSaveTime && !errorMessage && ( - - This report was automatically saved on + + This report was last saved on {' '} {lastSaveTime.format('MM/DD/YYYY [at] h:mm a')} diff --git a/frontend/src/components/Navigator/components/__tests__/SideNav.js b/frontend/src/components/Navigator/components/__tests__/SideNav.js index 051beb07c1..76d995042f 100644 --- a/frontend/src/components/Navigator/components/__tests__/SideNav.js +++ b/frontend/src/components/Navigator/components/__tests__/SideNav.js @@ -99,7 +99,7 @@ describe('SideNav', () => { it('the currently selected page has the current class', () => { renderNav(REPORT_STATUSES.SUBMITTED, () => {}, true); - const submitted = screen.getByRole('button', { name: 'test' }); + const submitted = screen.getByRole('button', { name: 'test Submitted' }); expect(submitted).toHaveClass('smart-hub--navigator-link-active'); }); }); diff --git a/frontend/src/components/Navigator/index.js b/frontend/src/components/Navigator/index.js index 3fe2d420ed..252639d778 100644 --- a/frontend/src/components/Navigator/index.js +++ b/frontend/src/components/Navigator/index.js @@ -6,7 +6,7 @@ */ import React, { useEffect, useState } from 'react'; import PropTypes from 'prop-types'; -import { FormProvider, useForm } from 'react-hook-form'; +import { FormProvider, useForm } from 'react-hook-form/dist/index.ie11'; import { Form, Button, @@ -46,7 +46,6 @@ function Navigator({ const [errorMessage, updateErrorMessage] = useState(); const [lastSaveTime, updateLastSaveTime] = useState(initialLastUpdated); const [showValidationErrors, updateShowValidationErrors] = useState(false); - const { pageState } = formData; const page = pages.find((p) => p.path === currentPage); const hookForm = useForm({ @@ -54,6 +53,7 @@ function Navigator({ defaultValues: formData, shouldUnregister: false, }); + const pageState = hookForm.watch('pageState'); const { formState, diff --git a/frontend/src/components/__tests__/DatePicker.js b/frontend/src/components/__tests__/DatePicker.js index c5e2fc2b79..72a97993e0 100644 --- a/frontend/src/components/__tests__/DatePicker.js +++ b/frontend/src/components/__tests__/DatePicker.js @@ -4,7 +4,7 @@ import { render, screen, fireEvent, waitFor, } from '@testing-library/react'; -import { useForm } from 'react-hook-form'; +import { useForm } from 'react-hook-form/dist/index.ie11'; import DatePicker from '../DatePicker'; // react-dates when opening the calendar in these tests. For details see diff --git a/frontend/src/components/__tests__/MutliSelect.js b/frontend/src/components/__tests__/MutliSelect.js index f08786654e..73abf56c46 100644 --- a/frontend/src/components/__tests__/MutliSelect.js +++ b/frontend/src/components/__tests__/MutliSelect.js @@ -3,7 +3,7 @@ import React from 'react'; import { render, screen } from '@testing-library/react'; import selectEvent from 'react-select-event'; import { act } from 'react-dom/test-utils'; -import { useForm } from 'react-hook-form'; +import { useForm } from 'react-hook-form/dist/index.ie11'; import userEvent from '@testing-library/user-event'; import { Label } from '@trussworks/react-uswds'; diff --git a/frontend/src/fetchers/index.js b/frontend/src/fetchers/index.js index 5b3a26eba6..edb25fcd03 100644 --- a/frontend/src/fetchers/index.js +++ b/frontend/src/fetchers/index.js @@ -10,6 +10,10 @@ export class HTTPError extends Error { export const get = async (url) => { const res = await fetch(url, { credentials: 'same-origin', + headers: { + 'Cache-Control': 'no-cache', + Pragma: 'no-cache', + }, }); if (!res.ok) { throw new HTTPError(res.status, res.statusText); diff --git a/frontend/src/pages/ActivityReport/Pages/Review/Approver/Review.js b/frontend/src/pages/ActivityReport/Pages/Review/Approver/Review.js index b741cd770a..c93acf432e 100644 --- a/frontend/src/pages/ActivityReport/Pages/Review/Approver/Review.js +++ b/frontend/src/pages/ActivityReport/Pages/Review/Approver/Review.js @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { useFormContext } from 'react-hook-form'; +import { useFormContext } from 'react-hook-form/dist/index.ie11'; import _ from 'lodash'; import { Dropdown, Form, Label, Fieldset, Textarea, Alert, Button, diff --git a/frontend/src/pages/ActivityReport/Pages/Review/Approver/__tests__/index.js b/frontend/src/pages/ActivityReport/Pages/Review/Approver/__tests__/index.js index 8c744b54df..f42238ef23 100644 --- a/frontend/src/pages/ActivityReport/Pages/Review/Approver/__tests__/index.js +++ b/frontend/src/pages/ActivityReport/Pages/Review/Approver/__tests__/index.js @@ -3,7 +3,7 @@ import '@testing-library/jest-dom'; import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; -import { FormProvider, useForm } from 'react-hook-form'; +import { FormProvider, useForm } from 'react-hook-form/dist/index.ie11'; import Approver from '../index'; import { REPORT_STATUSES } from '../../../../../../Constants'; diff --git a/frontend/src/pages/ActivityReport/Pages/Review/ReviewItem.js b/frontend/src/pages/ActivityReport/Pages/Review/ReviewItem.js index dc287d97f2..7f66804cdb 100644 --- a/frontend/src/pages/ActivityReport/Pages/Review/ReviewItem.js +++ b/frontend/src/pages/ActivityReport/Pages/Review/ReviewItem.js @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import _ from 'lodash'; -import { useFormContext } from 'react-hook-form'; +import { useFormContext } from 'react-hook-form/dist/index.ie11'; const ReviewItem = ({ label, name, path }) => { const { watch } = useFormContext(); diff --git a/frontend/src/pages/ActivityReport/Pages/Review/ReviewPage.js b/frontend/src/pages/ActivityReport/Pages/Review/ReviewPage.js index 0be343f134..4e598953fb 100644 --- a/frontend/src/pages/ActivityReport/Pages/Review/ReviewPage.js +++ b/frontend/src/pages/ActivityReport/Pages/Review/ReviewPage.js @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { some } from 'lodash'; -import { useFormContext } from 'react-hook-form'; +import { useFormContext } from 'react-hook-form/dist/index.ie11'; import Section from './ReviewSection'; import ReviewItem from './ReviewItem'; diff --git a/frontend/src/pages/ActivityReport/Pages/Review/Submitter/Draft.js b/frontend/src/pages/ActivityReport/Pages/Review/Submitter/Draft.js index b32f9e40e6..c08f072099 100644 --- a/frontend/src/pages/ActivityReport/Pages/Review/Submitter/Draft.js +++ b/frontend/src/pages/ActivityReport/Pages/Review/Submitter/Draft.js @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { useFormContext } from 'react-hook-form'; +import { useFormContext } from 'react-hook-form/dist/index.ie11'; import { Dropdown, Form, Fieldset, Textarea, Button, } from '@trussworks/react-uswds'; diff --git a/frontend/src/pages/ActivityReport/Pages/Review/Submitter/__tests__/index.js b/frontend/src/pages/ActivityReport/Pages/Review/Submitter/__tests__/index.js index e7b1d36b7a..383a83e5cf 100644 --- a/frontend/src/pages/ActivityReport/Pages/Review/Submitter/__tests__/index.js +++ b/frontend/src/pages/ActivityReport/Pages/Review/Submitter/__tests__/index.js @@ -3,7 +3,7 @@ import '@testing-library/jest-dom'; import { render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; -import { useForm, FormProvider } from 'react-hook-form'; +import { useForm, FormProvider } from 'react-hook-form/dist/index.ie11'; import Submitter from '../index'; import { REPORT_STATUSES } from '../../../../../../Constants'; diff --git a/frontend/src/pages/ActivityReport/Pages/Review/__tests__/ReviewPage.js b/frontend/src/pages/ActivityReport/Pages/Review/__tests__/ReviewPage.js index cbc0182181..c6a5bcf530 100644 --- a/frontend/src/pages/ActivityReport/Pages/Review/__tests__/ReviewPage.js +++ b/frontend/src/pages/ActivityReport/Pages/Review/__tests__/ReviewPage.js @@ -3,7 +3,7 @@ import React from 'react'; import '@testing-library/jest-dom'; import { render, screen } from '@testing-library/react'; import { BrowserRouter } from 'react-router-dom'; -import { FormProvider, useForm } from 'react-hook-form'; +import { FormProvider, useForm } from 'react-hook-form/dist/index.ie11'; import ReviewPage from '../ReviewPage'; const sections = [ diff --git a/frontend/src/pages/ActivityReport/Pages/Review/__tests__/index.js b/frontend/src/pages/ActivityReport/Pages/Review/__tests__/index.js index b6bcfc735a..3fb4b5f347 100644 --- a/frontend/src/pages/ActivityReport/Pages/Review/__tests__/index.js +++ b/frontend/src/pages/ActivityReport/Pages/Review/__tests__/index.js @@ -3,7 +3,7 @@ import '@testing-library/jest-dom'; import { render, screen, waitFor } from '@testing-library/react'; import React from 'react'; import userEvent from '@testing-library/user-event'; -import { FormProvider, useForm } from 'react-hook-form'; +import { FormProvider, useForm } from 'react-hook-form/dist/index.ie11'; import ReviewSubmit from '../index'; import { REPORT_STATUSES } from '../../../../../Constants'; diff --git a/frontend/src/pages/ActivityReport/Pages/__tests__/goalsObjectives.js b/frontend/src/pages/ActivityReport/Pages/__tests__/goalsObjectives.js index 8763f7af53..4299d0902b 100644 --- a/frontend/src/pages/ActivityReport/Pages/__tests__/goalsObjectives.js +++ b/frontend/src/pages/ActivityReport/Pages/__tests__/goalsObjectives.js @@ -3,7 +3,7 @@ import '@testing-library/jest-dom'; import { render, screen } from '@testing-library/react'; import fetchMock from 'fetch-mock'; import React from 'react'; -import { FormProvider, useForm } from 'react-hook-form'; +import { FormProvider, useForm } from 'react-hook-form/dist/index.ie11'; import join from 'url-join'; import { Router } from 'react-router-dom'; import { createMemoryHistory } from 'history'; @@ -14,18 +14,18 @@ const goalUrl = join('api', 'activity-reports', 'goals'); const RenderGoalsObjectives = ({ // eslint-disable-next-line react/prop-types - grantIds, activityRecipientType, initialData, + grantIds, activityRecipientType, }) => { + // eslint-disable-next-line react/prop-types + const activityRecipients = grantIds.map((activityRecipientId) => ({ activityRecipientId })); + const data = { activityRecipientType, activityRecipients }; const hookForm = useForm({ mode: 'onChange', - defaultValues: { goals: [], ...initialData }, + defaultValues: { goals: [], ...data }, }); - // eslint-disable-next-line react/prop-types - const activityRecipients = grantIds.map((id) => ({ activityRecipientId: id })); - const data = { ...initialData, activityRecipientType, activityRecipients }; return ( - {goalsObjectives.render({}, data)} + {goalsObjectives.render()} ); }; @@ -61,13 +61,13 @@ describe('goals objectives', () => { afterEach(() => fetchMock.restore()); describe('when activity recipient type is "grantee"', () => { it('the display goals section is displayed', async () => { - renderGoals([1], 'grantee', {}); + renderGoals([1], 'grantee'); await screen.findByText('Context'); expect(await screen.findByText('Goals and objectives')).toBeVisible(); }); it('the display goals section does not show if no grants are selected', async () => { - renderGoals([], 'grantee', {}); + renderGoals([], 'grantee'); await screen.findByText('Context'); expect(screen.queryByText('Goals and objectives')).toBeNull(); }); @@ -75,7 +75,7 @@ describe('goals objectives', () => { describe('when activity recipient type is not "grantee"', () => { it('the display goals section is not displayed', async () => { - renderGoals([1], 'nonGrantee', {}); + renderGoals([1], 'nonGrantee'); await screen.findByText('Context'); expect(screen.queryByText('Goals and objectives')).toBeNull(); }); diff --git a/frontend/src/pages/ActivityReport/Pages/__tests__/topicsResources.js b/frontend/src/pages/ActivityReport/Pages/__tests__/topicsResources.js index 75e0f52198..b43bb9cc06 100644 --- a/frontend/src/pages/ActivityReport/Pages/__tests__/topicsResources.js +++ b/frontend/src/pages/ActivityReport/Pages/__tests__/topicsResources.js @@ -2,7 +2,7 @@ import '@testing-library/jest-dom'; import { render, screen } from '@testing-library/react'; import React from 'react'; -import { FormProvider, useForm } from 'react-hook-form'; +import { FormProvider, useForm } from 'react-hook-form/dist/index.ie11'; import { Router } from 'react-router-dom'; import { createMemoryHistory } from 'history'; diff --git a/frontend/src/pages/ActivityReport/Pages/activitySummary.js b/frontend/src/pages/ActivityReport/Pages/activitySummary.js index 6959673bf1..bbc3c780c7 100644 --- a/frontend/src/pages/ActivityReport/Pages/activitySummary.js +++ b/frontend/src/pages/ActivityReport/Pages/activitySummary.js @@ -1,7 +1,7 @@ import React, { useEffect, useRef } from 'react'; import PropTypes from 'prop-types'; import { Helmet } from 'react-helmet'; -import { useFormContext } from 'react-hook-form'; +import { useFormContext } from 'react-hook-form/dist/index.ie11'; import { isEmpty } from 'lodash'; import { @@ -19,6 +19,7 @@ import { targetPopulations, } from '../constants'; import FormItem from '../../../components/FormItem'; +import { NOT_STARTED } from '../../../components/Navigator/constants'; const ActivitySummary = ({ recipients, @@ -34,6 +35,7 @@ const ActivitySummary = ({ const activityRecipientType = watch('activityRecipientType'); const startDate = watch('startDate'); const endDate = watch('endDate'); + const pageState = watch('pageState'); const isVirtual = watch('deliveryMethod') === 'virtual'; const { nonGrantees: rawNonGrantees, grants: rawGrants } = recipients; @@ -64,6 +66,12 @@ const ActivitySummary = ({ setValue('activityRecipients', []); setValue('participants', []); setValue('programTypes', []); + // Goals and objectives (page 3) has required fields when the recipient + // type is grantee, so we need to make sure that page is set as "not started" + // when recipient type is changed and we need to clear out any previously + // selected goals + setValue('goals', []); + setValue('pageState', { ...pageState, 3: NOT_STARTED }); previousActivityRecipientType.current = activityRecipientType; } }, [activityRecipientType, setValue]); @@ -229,6 +237,7 @@ const ActivitySummary = ({ name="startDate" > + <> {message} - + ); }; diff --git a/frontend/src/pages/ActivityReport/Pages/components/GoalPicker.js b/frontend/src/pages/ActivityReport/Pages/components/GoalPicker.js index db40e34f48..81c262b5d2 100644 --- a/frontend/src/pages/ActivityReport/Pages/components/GoalPicker.js +++ b/frontend/src/pages/ActivityReport/Pages/components/GoalPicker.js @@ -4,7 +4,7 @@ import PropTypes from 'prop-types'; import { Button, Label, TextInput, } from '@trussworks/react-uswds'; -import { useFormContext } from 'react-hook-form'; +import { useFormContext } from 'react-hook-form/dist/index.ie11'; import { v4 as uuidv4 } from 'uuid'; import FormItem from '../../../../components/FormItem'; @@ -36,7 +36,7 @@ const GoalPicker = ({ const onRemoveGoal = (id) => { const newGoals = selectedGoals.filter((selectedGoal) => selectedGoal.id !== id); - updateNewAvailableGoals((goals) => goals.filter((goal) => goal !== id)); + updateNewAvailableGoals(newGoals); setValue('goals', newGoals); }; @@ -98,6 +98,7 @@ const GoalPicker = ({ validate: validateGoals, }} options={uniqueAvailableGoals.map((goal) => ({ value: goal.id, label: goal.name }))} + singleRowInput multiSelectOptions={{ isClearable: false, closeMenuOnSelect: true, diff --git a/frontend/src/pages/ActivityReport/Pages/components/Objective.js b/frontend/src/pages/ActivityReport/Pages/components/Objective.js index 3a07e569a1..c252c2971a 100644 --- a/frontend/src/pages/ActivityReport/Pages/components/Objective.js +++ b/frontend/src/pages/ActivityReport/Pages/components/Objective.js @@ -1,6 +1,6 @@ import React, { useState, useRef, useEffect } from 'react'; import PropTypes from 'prop-types'; -import { useFormContext } from 'react-hook-form'; +import { useFormContext } from 'react-hook-form/dist/index.ie11'; import { Tag, Label, Button, TextInput, Dropdown, Grid, } from '@trussworks/react-uswds'; @@ -27,7 +27,7 @@ const Objective = ({ if (firstInput.current) { firstInput.current.focus(); } - }, [firstInput.current]); + }, []); const [editableObject, updateEditableObject] = useState(objective); const onChange = (e) => { diff --git a/frontend/src/pages/ActivityReport/Pages/components/ResourceSelector.js b/frontend/src/pages/ActivityReport/Pages/components/ResourceSelector.js index 3bcb61bf3e..1ecf39a2a0 100644 --- a/frontend/src/pages/ActivityReport/Pages/components/ResourceSelector.js +++ b/frontend/src/pages/ActivityReport/Pages/components/ResourceSelector.js @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { useFormContext, useFieldArray } from 'react-hook-form'; +import { useFormContext, useFieldArray } from 'react-hook-form/dist/index.ie11'; import { Button, TextInput } from '@trussworks/react-uswds'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; diff --git a/frontend/src/pages/ActivityReport/Pages/components/__tests__/Goal.js b/frontend/src/pages/ActivityReport/Pages/components/__tests__/Goal.js index 19f72e9399..d0625f141d 100644 --- a/frontend/src/pages/ActivityReport/Pages/components/__tests__/Goal.js +++ b/frontend/src/pages/ActivityReport/Pages/components/__tests__/Goal.js @@ -2,7 +2,7 @@ import '@testing-library/jest-dom'; import { render, screen, waitFor } from '@testing-library/react'; import React from 'react'; -import { FormProvider, useForm } from 'react-hook-form'; +import { FormProvider, useForm } from 'react-hook-form/dist/index.ie11'; import userEvent from '@testing-library/user-event'; import Goal from '../Goal'; diff --git a/frontend/src/pages/ActivityReport/Pages/components/__tests__/GoalPicker.js b/frontend/src/pages/ActivityReport/Pages/components/__tests__/GoalPicker.js index 8b41cd6010..fc16e17ffe 100644 --- a/frontend/src/pages/ActivityReport/Pages/components/__tests__/GoalPicker.js +++ b/frontend/src/pages/ActivityReport/Pages/components/__tests__/GoalPicker.js @@ -3,7 +3,7 @@ import '@testing-library/jest-dom'; import userEvent from '@testing-library/user-event'; import { render, screen, waitFor } from '@testing-library/react'; import React from 'react'; -import { FormProvider, useForm } from 'react-hook-form'; +import { FormProvider, useForm } from 'react-hook-form/dist/index.ie11'; import selectEvent from 'react-select-event'; import GoalPicker from '../GoalPicker'; diff --git a/frontend/src/pages/ActivityReport/Pages/components/__tests__/Objective.js b/frontend/src/pages/ActivityReport/Pages/components/__tests__/Objective.js index 882932ffeb..f842a5ca4b 100644 --- a/frontend/src/pages/ActivityReport/Pages/components/__tests__/Objective.js +++ b/frontend/src/pages/ActivityReport/Pages/components/__tests__/Objective.js @@ -2,7 +2,7 @@ import '@testing-library/jest-dom'; import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; -import { FormProvider, useForm } from 'react-hook-form'; +import { FormProvider, useForm } from 'react-hook-form/dist/index.ie11'; import Objective from '../Objective'; const RenderObjective = ({ diff --git a/frontend/src/pages/ActivityReport/Pages/components/__tests__/ResourceSelector.js b/frontend/src/pages/ActivityReport/Pages/components/__tests__/ResourceSelector.js index 21fd8fe60a..9ae4fef254 100644 --- a/frontend/src/pages/ActivityReport/Pages/components/__tests__/ResourceSelector.js +++ b/frontend/src/pages/ActivityReport/Pages/components/__tests__/ResourceSelector.js @@ -3,7 +3,7 @@ import '@testing-library/jest-dom'; import userEvent from '@testing-library/user-event'; import { render, screen, waitFor } from '@testing-library/react'; import React from 'react'; -import { FormProvider, useForm } from 'react-hook-form'; +import { FormProvider, useForm } from 'react-hook-form/dist/index.ie11'; import ResourceSelector from '../ResourceSelector'; diff --git a/frontend/src/pages/ActivityReport/Pages/goalsObjectives.js b/frontend/src/pages/ActivityReport/Pages/goalsObjectives.js index 9640810228..a5a744d074 100644 --- a/frontend/src/pages/ActivityReport/Pages/goalsObjectives.js +++ b/frontend/src/pages/ActivityReport/Pages/goalsObjectives.js @@ -1,11 +1,10 @@ import React, { useState } from 'react'; -import PropTypes from 'prop-types'; import { Helmet } from 'react-helmet'; import { Fieldset, Label, Textarea, } from '@trussworks/react-uswds'; import useDeepCompareEffect from 'use-deep-compare-effect'; -import { useFormContext } from 'react-hook-form'; +import { useFormContext } from 'react-hook-form/dist/index.ie11'; import { isUndefined } from 'lodash'; import ReviewItem from './Review/ReviewItem'; @@ -14,18 +13,21 @@ import GoalPicker from './components/GoalPicker'; import { getGoals } from '../../../fetchers/activityReports'; import { validateGoals } from './components/goalValidator'; -const GoalsObjectives = ({ - grantIds, activityRecipientType, -}) => { +const GoalsObjectives = () => { const { - register, + register, watch, } = useFormContext(); + const recipients = watch('activityRecipients'); + const activityRecipientType = watch('activityRecipientType'); + const recipientGrantee = activityRecipientType === 'grantee'; + const grantIds = recipientGrantee ? recipients.map((r) => r.activityRecipientId) : []; + const [availableGoals, updateAvailableGoals] = useState([]); const hasGrants = grantIds.length > 0; useDeepCompareEffect(() => { const fetch = async () => { - if (activityRecipientType === 'grantee' && hasGrants) { + if (recipientGrantee && hasGrants) { const fetchedGoals = await getGoals(grantIds); updateAvailableGoals(fetchedGoals); } @@ -33,12 +35,14 @@ const GoalsObjectives = ({ fetch(); }, [grantIds]); + const showGoals = recipientGrantee && hasGrants; + return ( <> Goals and objectives - {activityRecipientType === 'grantee' && hasGrants + {showGoals && (
@@ -49,21 +53,17 @@ const GoalsObjectives = ({ )}
-