From 36404a8a9b057a9056a641b07cfffa54ee6cbc83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yusuf=20Co=C5=9Fkun?= Date: Tue, 16 Jun 2020 20:17:02 +0300 Subject: [PATCH] NCI-Agency/anet#3024: Make all-day logic more obvious in report form Add allDay and fullWidth props to DatePicker in order to provide needed functionality for all-day events. --- client/src/components/CustomDateInput.js | 24 ++- client/src/index.css | 2 +- .../reports/EngagementDateFormPartial.js | 156 ++++++++++++++---- client/tests/e2e/report.js | 6 +- 4 files changed, 143 insertions(+), 45 deletions(-) diff --git a/client/src/components/CustomDateInput.js b/client/src/components/CustomDateInput.js index 45399c5b412..2d8d17da355 100644 --- a/client/src/components/CustomDateInput.js +++ b/client/src/components/CustomDateInput.js @@ -30,18 +30,21 @@ const CustomDateInput = ({ withTime, value, onChange, - onBlur + onBlur, + fullWidth, + allDay }) => { const inputRef = useRef() const rightElement = showIcon && CalendarIcon(inputRef.current) const width = 8 + (showIcon ? 3 : 0) + (withTime ? 3 : 0) - const style = { width: `${width}em`, fontSize: "1.1em" } - const dateFormats = withTime - ? Settings.dateFormats.forms.input.withTime - : Settings.dateFormats.forms.input.date + const style = { width: fullWidth ? "100%" : `${width}em`, fontSize: "1.1em" } + const dateFormats = + withTime && !allDay + ? Settings.dateFormats.forms.input.withTime + : Settings.dateFormats.forms.input.date const inputFormat = dateFormats[0] const timePickerProps = !withTime - ? {} + ? undefined : { precision: TimePrecision.MINUTE, selectAllOnFocus: true @@ -70,6 +73,7 @@ const CustomDateInput = ({ timePickerProps={timePickerProps} popoverProps={{ usePortal: false }} disabled={disabled} + fill={fullWidth} /> ) } @@ -84,12 +88,16 @@ CustomDateInput.propTypes = { PropTypes.instanceOf(Date) ]), onChange: PropTypes.func, - onBlur: PropTypes.func + onBlur: PropTypes.func, + fullWidth: PropTypes.bool, + allDay: PropTypes.bool } CustomDateInput.defaultProps = { disabled: false, showIcon: true, - withTime: false + withTime: false, + fullWidth: false, + allDay: true } export default CustomDateInput diff --git a/client/src/index.css b/client/src/index.css index 334ad06d492..58ebb7ebef4 100644 --- a/client/src/index.css +++ b/client/src/index.css @@ -147,7 +147,7 @@ fieldset.danger { } #duration { - width: 10em; + width: 100%; } .shortcut-list { diff --git a/client/src/pages/reports/EngagementDateFormPartial.js b/client/src/pages/reports/EngagementDateFormPartial.js index 16f6d56d16c..75a4f810c72 100644 --- a/client/src/pages/reports/EngagementDateFormPartial.js +++ b/client/src/pages/reports/EngagementDateFormPartial.js @@ -1,10 +1,12 @@ +import { Checkbox } from "@blueprintjs/core" import CustomDateInput from "components/CustomDateInput" import * as FieldHelper from "components/FieldHelper" -import { FastField } from "formik" +import { FastField, Field } from "formik" import { Report } from "models" +import moment from "moment" import PropTypes from "prop-types" -import React from "react" -import { HelpBlock } from "react-bootstrap" +import React, { useEffect, useState } from "react" +import { Col, ControlLabel, FormGroup, HelpBlock } from "react-bootstrap" import Settings from "settings" const futureEngagementHint = ( @@ -13,6 +15,114 @@ const futureEngagementHint = ( ) +function isStartOfDay(date) { + return date && moment(date).isSame(moment(date).startOf("day")) +} + +const EngagementDatePartialFormWithDuration = ({ + setFieldValue, + setFieldTouched, + validateFieldDebounced, + initialValues, + edit, + values +}) => { + const [isAllDay, setIsAllDay] = useState(true) + useEffect(() => { + if (!edit || !initialValues.engagementDate) { + setIsAllDay(true) + } else if (!isStartOfDay(initialValues.engagementDate)) { + setIsAllDay(false) + } else { + setIsAllDay(initialValues.duration === null) + } + // this logic should run only once when the component is mounted + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []) + + return ( + + + Engagement planning + + + { + const sval = value ? moment(value).startOf("minute").toDate() : null + setFieldTouched("engagementDate", true, false) // onBlur doesn't work when selecting a date + setFieldValue("engagementDate", sval, true) + if (!sval) { + setIsAllDay(true) + } else if (!isStartOfDay(sval)) { + setIsAllDay(false) + } + }} + onBlur={() => setFieldTouched("engagementDate")} + widget={ + + } + vertical + > + {Report.isFuture(values.engagementDate) && futureEngagementHint} + + + + { + const safeVal = + (event.target.value || "").replace(/[^0-9]+/g, "") || null + setFieldTouched("duration", true, false) + setFieldValue("duration", safeVal, false) + validateFieldDebounced("duration") + setIsAllDay(false) + }} + vertical + disabled={isAllDay} + /> + + + { + setIsAllDay(e.target.checked) + if (e.target.checked) { + setFieldValue("duration", null, true) + validateFieldDebounced("duration") + if (values.engagementDate) { + setFieldValue( + "engagementDate", + moment(values.engagementDate).startOf("day").toDate(), + false + ) + } + } + }} + /> + + + ) +} + +EngagementDatePartialFormWithDuration.propTypes = { + setFieldValue: PropTypes.func.isRequired, + setFieldTouched: PropTypes.func.isRequired, + validateFieldDebounced: PropTypes.func.isRequired, + values: PropTypes.object.isRequired, + initialValues: PropTypes.instanceOf(Report).isRequired, + edit: PropTypes.bool.isRequired +} + const EngagementDateFormPartial = ({ setFieldValue, setFieldTouched, @@ -27,10 +137,9 @@ const EngagementDateFormPartial = ({ name="engagementDate" component={FieldHelper.SpecialField} onChange={value => { - setFieldTouched("engagementDate", true, false) // onBlur doesn't work when selecting a date - setFieldValue("engagementDate", value, true) + const val = value ? moment(value).startOf("day").toDate() : null + setFieldValue("engagementDate", val, true) }} - onBlur={() => setFieldTouched("engagementDate")} widget={} > {Report.isFuture(values.engagementDate) && futureEngagementHint} @@ -39,33 +148,14 @@ const EngagementDateFormPartial = ({ } return ( - <> - { - setFieldTouched("engagementDate", true, false) // onBlur doesn't work when selecting a date - setFieldValue("engagementDate", value, true) - }} - onBlur={() => setFieldTouched("engagementDate")} - widget={} - > - {Report.isFuture(values.engagementDate) && futureEngagementHint} - - - { - const safeVal = - (event.target.value || "").replace(/[^0-9]+/g, "") || null - setFieldTouched("duration", true, false) - setFieldValue("duration", safeVal, false) - validateFieldDebounced("duration") - }} - /> - + ) } diff --git a/client/tests/e2e/report.js b/client/tests/e2e/report.js index b1ae0ebef2e..ebb1644ba42 100644 --- a/client/tests/e2e/report.js +++ b/client/tests/e2e/report.js @@ -35,8 +35,8 @@ test.serial("Draft and submit a report", async t => { await pageHelpers.clickTodayButton() - const $intent = await $("#intent") - await $intent.click() // click intent to make sure the date picker is being closed + const $intent = await $('label[for="intent"]') + await $intent.click() // click intent label to make sure the date picker is being closed await pageHelpers.writeInForm("#duration", "30") await t.context.driver.sleep(shortWaitMs) // wait for the datepicker to pop up @@ -456,7 +456,7 @@ test.serial( ) await $input.sendKeys("user input") - await $input.sendKeys(t.context.Key.TAB) // fire blur event + await $searchBarInput.click() // fire blur event t.false( _includes(await $fieldGroup.getAttribute("class"), warningClass), `After typing in ${fieldName} field, warning state goes away`