From 1ed37af252aa7a59023bbdce69326e565051cb65 Mon Sep 17 00:00:00 2001 From: Matt Bevilacqua Date: Wed, 2 Jun 2021 16:24:35 -0400 Subject: [PATCH 1/6] start date is now passed a calculated allowed end date --- .../ActivityReport/Pages/activitySummary.js | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/frontend/src/pages/ActivityReport/Pages/activitySummary.js b/frontend/src/pages/ActivityReport/Pages/activitySummary.js index 5b97e54f7d..7f8630cc81 100644 --- a/frontend/src/pages/ActivityReport/Pages/activitySummary.js +++ b/frontend/src/pages/ActivityReport/Pages/activitySummary.js @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import { Helmet } from 'react-helmet'; import { useFormContext } from 'react-hook-form/dist/index.ie11'; import { isEmpty } from 'lodash'; - +import moment from 'moment'; import { Fieldset, Radio, Grid, TextInput, Checkbox, } from '@trussworks/react-uswds'; @@ -39,6 +39,20 @@ const ActivitySummary = ({ const isVirtual = watch('deliveryMethod') === 'virtual'; const { nonGrantees: rawNonGrantees, grants: rawGrants } = recipients; + /** + * + * if "endDate" is passed into the Start Date Date picker as is, + * then it is excluded as an option on that start date date picker + * so we calculate the day after using the included moment library + * and convert it to a string to pass prop types validation + * + * that way once start and end date are entered, you can still edit the + * start date to be the same as end date + * + */ + + const allowedEndDate = (selectedEndDate) => (selectedEndDate ? moment(endDate).add(1, 'days').format('MM/DD/YYYY') : null); + const grants = rawGrants.map((grantee) => ({ label: grantee.name, options: grantee.grants.map((grant) => ({ @@ -242,7 +256,7 @@ const ActivitySummary = ({ From 13d4887dd302b053908b8a23606c5cf6a4646f35 Mon Sep 17 00:00:00 2001 From: Matt Bevilacqua Date: Thu, 3 Jun 2021 11:25:17 -0400 Subject: [PATCH 2/6] incorporate feedback to increase reusability --- frontend/src/components/DatePicker.js | 17 +++++++++++++++-- .../ActivityReport/Pages/activitySummary.js | 19 ++----------------- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/frontend/src/components/DatePicker.js b/frontend/src/components/DatePicker.js index dfa1cb9688..edb57047b5 100644 --- a/frontend/src/components/DatePicker.js +++ b/frontend/src/components/DatePicker.js @@ -22,7 +22,7 @@ import './DatePicker.css'; const dateFmt = 'MM/DD/YYYY'; const DateInput = ({ - control, minDate, name, disabled, maxDate, openUp, required, ariaName, + control, minDate, name, disabled, maxDate, openUp, required, ariaName, maxDateInclusive, }) => { const hintId = `${name}-hint`; const [isFocused, updateFocus] = useState(false); @@ -30,7 +30,18 @@ const DateInput = ({ const isOutsideRange = (date) => { const isBefore = minDate && date.isBefore(moment(minDate, dateFmt)); - const isAfter = maxDate && date.isAfter(moment(maxDate, dateFmt)); + // console.log({name, maxDate: moment(maxDate, "DD-MM-YYYY").add(1, 'days')}); + + // If max date is inclusive (maxDateInclusive == true) + // allow the user to pick a start date that is the same as the maxDate + // otherwise, only the day before is allowed + let isAfter = false; + if (maxDateInclusive) { + const newDate = moment(maxDate, dateFmt).add(1, 'days'); + isAfter = maxDate && date.isAfter(newDate, dateFmt); + } else { + isAfter = maxDate && date.isAfter(moment(maxDate, dateFmt)); + } return isBefore || isAfter; }; @@ -103,6 +114,7 @@ DateInput.propTypes = { openUp: PropTypes.bool, disabled: PropTypes.bool, required: PropTypes.bool, + maxDateInclusive: PropTypes.bool, }; DateInput.defaultProps = { @@ -111,6 +123,7 @@ DateInput.defaultProps = { disabled: false, openUp: false, required: true, + maxDateInclusive: false, }; export default DateInput; diff --git a/frontend/src/pages/ActivityReport/Pages/activitySummary.js b/frontend/src/pages/ActivityReport/Pages/activitySummary.js index 7f8630cc81..9597bfc0ab 100644 --- a/frontend/src/pages/ActivityReport/Pages/activitySummary.js +++ b/frontend/src/pages/ActivityReport/Pages/activitySummary.js @@ -3,11 +3,9 @@ import PropTypes from 'prop-types'; import { Helmet } from 'react-helmet'; import { useFormContext } from 'react-hook-form/dist/index.ie11'; import { isEmpty } from 'lodash'; -import moment from 'moment'; import { Fieldset, Radio, Grid, TextInput, Checkbox, } from '@trussworks/react-uswds'; - import ReviewPage from './Review/ReviewPage'; import DatePicker from '../../../components/DatePicker'; import MultiSelect from '../../../components/MultiSelect'; @@ -39,20 +37,6 @@ const ActivitySummary = ({ const isVirtual = watch('deliveryMethod') === 'virtual'; const { nonGrantees: rawNonGrantees, grants: rawGrants } = recipients; - /** - * - * if "endDate" is passed into the Start Date Date picker as is, - * then it is excluded as an option on that start date date picker - * so we calculate the day after using the included moment library - * and convert it to a string to pass prop types validation - * - * that way once start and end date are entered, you can still edit the - * start date to be the same as end date - * - */ - - const allowedEndDate = (selectedEndDate) => (selectedEndDate ? moment(endDate).add(1, 'days').format('MM/DD/YYYY') : null); - const grants = rawGrants.map((grantee) => ({ label: grantee.name, options: grantee.grants.map((grant) => ({ @@ -256,8 +240,9 @@ const ActivitySummary = ({ From 2b436e440ea565d29bd201a1c8b9869570ca3990 Mon Sep 17 00:00:00 2001 From: Matt Bevilacqua Date: Thu, 3 Jun 2021 11:27:47 -0400 Subject: [PATCH 3/6] remove console log --- frontend/src/components/DatePicker.js | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/src/components/DatePicker.js b/frontend/src/components/DatePicker.js index edb57047b5..261ea029d7 100644 --- a/frontend/src/components/DatePicker.js +++ b/frontend/src/components/DatePicker.js @@ -30,7 +30,6 @@ const DateInput = ({ const isOutsideRange = (date) => { const isBefore = minDate && date.isBefore(moment(minDate, dateFmt)); - // console.log({name, maxDate: moment(maxDate, "DD-MM-YYYY").add(1, 'days')}); // If max date is inclusive (maxDateInclusive == true) // allow the user to pick a start date that is the same as the maxDate From 7a31c2ce398e95f46b2fd83094858800b3ed4f87 Mon Sep 17 00:00:00 2001 From: Matt Bevilacqua Date: Fri, 4 Jun 2021 07:59:28 -0400 Subject: [PATCH 4/6] draft unit test --- .../src/components/__tests__/DatePicker.js | 23 ++++++++++++++++++- .../ActivityReport/Pages/activitySummary.js | 5 ++-- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/frontend/src/components/__tests__/DatePicker.js b/frontend/src/components/__tests__/DatePicker.js index 45bde3263e..c4552f8832 100644 --- a/frontend/src/components/__tests__/DatePicker.js +++ b/frontend/src/components/__tests__/DatePicker.js @@ -3,6 +3,7 @@ import React from 'react'; import { render, screen, fireEvent, waitFor, } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; import { useForm } from 'react-hook-form/dist/index.ie11'; import DatePicker from '../DatePicker'; @@ -28,7 +29,7 @@ Object.defineProperty(window, 'getComputedStyle', { describe('DatePicker', () => { // eslint-disable-next-line react/prop-types - const RenderDatePicker = ({ disabled }) => { + const RenderDatePicker = ({ disabled, maxDate, maxDateInclusive }) => { const { control } = useForm(); return (
@@ -38,6 +39,8 @@ describe('DatePicker', () => { name="picker" disabled={disabled} ariaName="datepicker" + maxDateInclusive={maxDateInclusive} + maxDate={maxDate} />
); @@ -62,4 +65,22 @@ describe('DatePicker', () => { const button = await screen.findByLabelText('Move backward to switch to the previous month.'); await waitFor(() => expect(button).toBeVisible()); }); + + it('correctly accounts for the maxDateInclusive prop being false', async () => { + render(); + + const text = await screen.findByRole('textbox'); + userEvent.type(text, '02/02/2022'); + + await waitFor(() => expect(text).toBeInvalid()); + }); + + it('correctly accounts for the maxDateInclusive prop being true', async () => { + render(); + + const text = await screen.findByRole('textbox'); + userEvent.type(text, '02/02/2022'); + + expect(text).toBeValid(); + }); }); diff --git a/frontend/src/pages/ActivityReport/Pages/activitySummary.js b/frontend/src/pages/ActivityReport/Pages/activitySummary.js index 9597bfc0ab..474b9079a0 100644 --- a/frontend/src/pages/ActivityReport/Pages/activitySummary.js +++ b/frontend/src/pages/ActivityReport/Pages/activitySummary.js @@ -32,7 +32,7 @@ const ActivitySummary = ({ } = useFormContext(); const activityRecipientType = watch('activityRecipientType'); const startDate = watch('startDate'); - const endDate = watch('endDate'); + // const endDate = watch('endDate'); const pageState = watch('pageState'); const isVirtual = watch('deliveryMethod') === 'virtual'; const { nonGrantees: rawNonGrantees, grants: rawGrants } = recipients; @@ -240,9 +240,8 @@ const ActivitySummary = ({ From de5e9ecb6b8f8578782c4b05d0ca4916f95ef55e Mon Sep 17 00:00:00 2001 From: Matt Bevilacqua Date: Fri, 4 Jun 2021 09:50:36 -0400 Subject: [PATCH 5/6] add unit test --- .../src/components/__tests__/DatePicker.js | 20 ------------- .../ActivityReport/Pages/activitySummary.js | 5 ++-- .../pages/ActivityReport/__tests__/index.js | 30 +++++++++++++++++++ 3 files changed, 33 insertions(+), 22 deletions(-) diff --git a/frontend/src/components/__tests__/DatePicker.js b/frontend/src/components/__tests__/DatePicker.js index c4552f8832..44ed1890e7 100644 --- a/frontend/src/components/__tests__/DatePicker.js +++ b/frontend/src/components/__tests__/DatePicker.js @@ -3,8 +3,6 @@ import React from 'react'; import { render, screen, fireEvent, waitFor, } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; - import { useForm } from 'react-hook-form/dist/index.ie11'; import DatePicker from '../DatePicker'; @@ -65,22 +63,4 @@ describe('DatePicker', () => { const button = await screen.findByLabelText('Move backward to switch to the previous month.'); await waitFor(() => expect(button).toBeVisible()); }); - - it('correctly accounts for the maxDateInclusive prop being false', async () => { - render(); - - const text = await screen.findByRole('textbox'); - userEvent.type(text, '02/02/2022'); - - await waitFor(() => expect(text).toBeInvalid()); - }); - - it('correctly accounts for the maxDateInclusive prop being true', async () => { - render(); - - const text = await screen.findByRole('textbox'); - userEvent.type(text, '02/02/2022'); - - expect(text).toBeValid(); - }); }); diff --git a/frontend/src/pages/ActivityReport/Pages/activitySummary.js b/frontend/src/pages/ActivityReport/Pages/activitySummary.js index 474b9079a0..a8e19a89bd 100644 --- a/frontend/src/pages/ActivityReport/Pages/activitySummary.js +++ b/frontend/src/pages/ActivityReport/Pages/activitySummary.js @@ -32,7 +32,7 @@ const ActivitySummary = ({ } = useFormContext(); const activityRecipientType = watch('activityRecipientType'); const startDate = watch('startDate'); - // const endDate = watch('endDate'); + const endDate = watch('endDate'); const pageState = watch('pageState'); const isVirtual = watch('deliveryMethod') === 'virtual'; const { nonGrantees: rawNonGrantees, grants: rawGrants } = recipients; @@ -240,7 +240,8 @@ const ActivitySummary = ({ diff --git a/frontend/src/pages/ActivityReport/__tests__/index.js b/frontend/src/pages/ActivityReport/__tests__/index.js index ca8b8912fe..fd2c1c549c 100644 --- a/frontend/src/pages/ActivityReport/__tests__/index.js +++ b/frontend/src/pages/ActivityReport/__tests__/index.js @@ -209,5 +209,35 @@ describe('ActivityReport', () => { expect(screen.queryByText(withText('Grantee Name'))).toBeNull(); }); + + it('allows you to pick the same start and end date', async () => { + // render a new activity report + renderActivityReport('new'); + + // we need to wait for the page to render, that's what this is for + const dateSection = await screen.findByRole('group', { name: 'Activity date' }); + + // get the start date text box and type in a date + const startDate = within(dateSection).getByRole('textbox', { name: /start date \(required\), month\/day\/year, edit text/i }); + userEvent.type(startDate, '12/25/1967'); + + // then type in a different date in the end date box + const endDate = within(dateSection).getByRole('textbox', { name: /end date \(required\), month\/day\/year, edit text/i }); + userEvent.type(endDate, '12/26/1967'); + + // then change the start date to a date after the end date + userEvent.clear(startDate); + userEvent.type(startDate, '12/28/1967'); + + // expect an error + expect(endDate).toBeDisabled(); + + // then change the start date to a date after the end date + userEvent.clear(startDate); + userEvent.type(startDate, '12/26/1967'); + + // expect everything to be ok + expect(endDate).toBeEnabled(); + }); }); }); From c9579590b0c18e22260e3fa4dc2a2ba58a340635 Mon Sep 17 00:00:00 2001 From: Matt Bevilacqua Date: Fri, 4 Jun 2021 10:29:11 -0400 Subject: [PATCH 6/6] put the date picker test back the way I found it --- frontend/src/components/__tests__/DatePicker.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/frontend/src/components/__tests__/DatePicker.js b/frontend/src/components/__tests__/DatePicker.js index 44ed1890e7..7418b0e89a 100644 --- a/frontend/src/components/__tests__/DatePicker.js +++ b/frontend/src/components/__tests__/DatePicker.js @@ -27,7 +27,7 @@ Object.defineProperty(window, 'getComputedStyle', { describe('DatePicker', () => { // eslint-disable-next-line react/prop-types - const RenderDatePicker = ({ disabled, maxDate, maxDateInclusive }) => { + const RenderDatePicker = ({ disabled }) => { const { control } = useForm(); return (
@@ -37,8 +37,6 @@ describe('DatePicker', () => { name="picker" disabled={disabled} ariaName="datepicker" - maxDateInclusive={maxDateInclusive} - maxDate={maxDate} />
);