From 3715cd99e53f7d1d96bbf27e28b03fdc3069ed9e Mon Sep 17 00:00:00 2001 From: Josh Salisbury Date: Mon, 26 Oct 2020 17:17:22 -0500 Subject: [PATCH 1/6] Add first parts of activity report. Remove stepper * Stepper is removed for now. We will need to support out of order form entry, which the stepper doesn't support (and can't easily be updated to do so) --- .circleci/config.yml | 2 +- frontend/package.json | 8 +- frontend/src/components/DatePicker.css | 3 + frontend/src/components/DatePicker.js | 108 +++++ frontend/src/components/FileUploader.js | 100 +++++ frontend/src/components/MultiSelect.css | 9 + frontend/src/components/MultiSelect.js | 70 +++ frontend/src/components/Stepper/Pager.js | 77 ---- .../src/components/Stepper/__tests__/Pager.js | 77 ---- .../src/components/Stepper/__tests__/index.js | 77 ---- .../components/Stepper/components/Footer.js | 29 -- .../Stepper/components/StepperHeader.js | 32 -- .../Stepper/components/StepperIndicator.css | 3 - .../Stepper/components/StepperIndicator.js | 46 -- .../components/StepperIndicatorSegment.js | 37 -- .../Stepper/components/__tests__/Footer.js | 54 --- .../components/__tests__/StepperHeader.js | 19 - .../components/__tests__/StepperIndicator.js | 48 -- frontend/src/components/Stepper/index.js | 146 ------ .../src/components/__tests__/DatePicker.js | 59 +++ .../src/components/__tests__/FileUploader.js | 62 +++ frontend/src/images/arrow-both.svg | 1 + .../src/pages/ActivityReport/SectionOne.js | 149 +++++++ .../src/pages/ActivityReport/SectionThree.js | 140 ++++++ .../src/pages/ActivityReport/SectionTwo.js | 68 +++ .../pages/ActivityReport/__tests__/index.js | 84 ++++ frontend/src/pages/ActivityReport/index.css | 16 + frontend/src/pages/ActivityReport/index.js | 127 +++--- frontend/src/pages/ActivityReport/step.js | 43 -- frontend/yarn.lock | 414 +++++++++++++++++- 30 files changed, 1339 insertions(+), 769 deletions(-) create mode 100644 frontend/src/components/DatePicker.css create mode 100644 frontend/src/components/DatePicker.js create mode 100644 frontend/src/components/FileUploader.js create mode 100644 frontend/src/components/MultiSelect.css create mode 100644 frontend/src/components/MultiSelect.js delete mode 100644 frontend/src/components/Stepper/Pager.js delete mode 100644 frontend/src/components/Stepper/__tests__/Pager.js delete mode 100644 frontend/src/components/Stepper/__tests__/index.js delete mode 100644 frontend/src/components/Stepper/components/Footer.js delete mode 100644 frontend/src/components/Stepper/components/StepperHeader.js delete mode 100644 frontend/src/components/Stepper/components/StepperIndicator.css delete mode 100644 frontend/src/components/Stepper/components/StepperIndicator.js delete mode 100644 frontend/src/components/Stepper/components/StepperIndicatorSegment.js delete mode 100644 frontend/src/components/Stepper/components/__tests__/Footer.js delete mode 100644 frontend/src/components/Stepper/components/__tests__/StepperHeader.js delete mode 100644 frontend/src/components/Stepper/components/__tests__/StepperIndicator.js delete mode 100644 frontend/src/components/Stepper/index.js create mode 100644 frontend/src/components/__tests__/DatePicker.js create mode 100644 frontend/src/components/__tests__/FileUploader.js create mode 100644 frontend/src/images/arrow-both.svg create mode 100644 frontend/src/pages/ActivityReport/SectionOne.js create mode 100644 frontend/src/pages/ActivityReport/SectionThree.js create mode 100644 frontend/src/pages/ActivityReport/SectionTwo.js create mode 100644 frontend/src/pages/ActivityReport/__tests__/index.js delete mode 100644 frontend/src/pages/ActivityReport/step.js diff --git a/.circleci/config.yml b/.circleci/config.yml index 7e3fcf36ce..1ce568f3e8 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -109,7 +109,7 @@ parameters: default: "main" type: string sandbox_git_branch: # change to feature branch to test deployment - default: "sj-update-cf-org" + default: "js-81-first-activity-report-fields" type: string jobs: build: diff --git a/frontend/package.json b/frontend/package.json index 350185c311..aadf45b5b4 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -3,14 +3,20 @@ "version": "0.1.0", "private": true, "dependencies": { + "@fortawesome/fontawesome-free": "^5.15.1", + "@fortawesome/fontawesome-svg-core": "^1.2.32", + "@fortawesome/free-solid-svg-icons": "^5.15.1", + "@fortawesome/react-fontawesome": "^0.1.11", "@testing-library/jest-dom": "^4.2.4", "@trussworks/react-uswds": "^1.9.1", "http-proxy-middleware": "^1.0.5", "lodash": "^4.17.20", "prop-types": "^15.7.2", "react": "^16.13.1", + "react-datepicker": "^3.3.0", "react-dom": "^16.13.1", - "react-hook-form": "^6.9.1", + "react-dropzone": "^11.2.0", + "react-hook-form": "^6.9.0", "react-router": "^5.2.0", "react-router-dom": "^5.2.0", "react-router-prop-types": "^1.0.5", diff --git a/frontend/src/components/DatePicker.css b/frontend/src/components/DatePicker.css new file mode 100644 index 0000000000..e7f4ece461 --- /dev/null +++ b/frontend/src/components/DatePicker.css @@ -0,0 +1,3 @@ +.usa-date-picker__button { + margin-top: 0.5rem +} \ No newline at end of file diff --git a/frontend/src/components/DatePicker.js b/frontend/src/components/DatePicker.js new file mode 100644 index 0000000000..f7f9908003 --- /dev/null +++ b/frontend/src/components/DatePicker.js @@ -0,0 +1,108 @@ +/* + This component requires being embedded into a `react-hook-form` form + + Uses ReactDatePicker styled as the USWDS date picker. The react USWDS library does + not have a date picker component. We could have used USWDS component directly here + instead of ReactDatePicker but I decided against for a couple reasons: + 1. I was having a hard time getting input back into react hook form using the USWDS + code directly. Issue centered around the USWDS code not sending `onChange` events + when an invalid date was input + 2. Related to #1, ReactDatePicker handles invalid dates by removing the invalid input + on blur, which is nicer then how the USWDS component handled invalid dates. + 3. ReactDatePicker had easily readable documentation and conveniences such as `maxDate` + and `minDate`. I couldn't find great docs using the USWDS datepicker javascript +*/ + +import React, { forwardRef } from 'react'; +import PropTypes from 'prop-types'; +import { TextInput, Label } from '@trussworks/react-uswds'; +import ReactDatePicker from 'react-datepicker'; +import { Controller } from 'react-hook-form'; + +import 'react-datepicker/dist/react-datepicker.css'; +import './DatePicker.css'; + +const DateInput = ({ + control, label, minDate, name, disabled, maxDate, +}) => { + const labelId = `${name}-id`; + const hintId = `${name}-hint`; + + const CustomInput = forwardRef(({ value, onChange, onFocus }, ref) => ( +
+ +
+ )); + + CustomInput.propTypes = { + value: PropTypes.string, + onChange: PropTypes.func, + onFocus: PropTypes.func, + }; + + CustomInput.defaultProps = { + value: undefined, + onChange: undefined, + onFocus: undefined, + }; + + return ( + <> + +
mm/dd/yyyy
+ ( + } + dropdownMode="select" + placeholderText="Click to select time" + shouldCloseOnSelect + /> + )} + control={control} + name={name} + disabled={disabled} + defaultValue={null} + rules={{ + required: true, + }} + /> + + ); +}; + +DateInput.propTypes = { + // control is an object from react-hook-form + // eslint-disable-next-line react/forbid-prop-types + control: PropTypes.object.isRequired, + name: PropTypes.string.isRequired, + label: PropTypes.string.isRequired, + minDate: PropTypes.instanceOf(Date), + maxDate: PropTypes.instanceOf(Date), + disabled: PropTypes.bool, +}; + +DateInput.defaultProps = { + minDate: undefined, + maxDate: undefined, + disabled: false, +}; + +export default DateInput; diff --git a/frontend/src/components/FileUploader.js b/frontend/src/components/FileUploader.js new file mode 100644 index 0000000000..3e427f9548 --- /dev/null +++ b/frontend/src/components/FileUploader.js @@ -0,0 +1,100 @@ +/* + Uses `react-dropzone` to allow file uploads. Must be placed inside a `react-hook-form` + form + + This component will likely see style updates, specifically around the list of already + selected files. +*/ +// react-dropzone examples all use prop spreading. Disabling the eslint no prop spreading +// rules https://github.com/react-dropzone/react-dropzone +/* eslint-disable react/jsx-props-no-spreading */ +import React from 'react'; +import PropTypes from 'prop-types'; +import { useDropzone } from 'react-dropzone'; + +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faTimes } from '@fortawesome/free-solid-svg-icons'; + +import { Button } from '@trussworks/react-uswds'; + +function Dropzone(props) { + const { onChange } = props; + const onDrop = (e) => { + onChange(e); + }; + const { getRootProps, getInputProps } = useDropzone({ onDrop }); + + // I tried moving these styles to a css file and applying a class to the container + // and span. The styles were not being applied, it seems like the Dropzone library + // is messing with the styles somewhere + const containerStyle = { + width: '31rem', + height: '8rem', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + borderStyle: 'dashed', + borderWidth: '0.125rem', + borderColor: '#979797', + }; + + const linkStyle = { + cursor: 'pointer', + color: 'blue', + textDecoration: 'underline', + }; + + return ( +
+ +

+ Drag file here or + {' '} + choose from folder +

+
+ ); +} + +Dropzone.propTypes = { + onChange: PropTypes.func.isRequired, +}; + +const FileUploader = ({ onChange, files }) => { + const onFilesAdded = (newFiles) => { + onChange([...files, ...newFiles]); + }; + + const onFileRemoved = (removedFileIndex) => { + onChange(files.filter((f, index) => (index !== removedFileIndex))); + }; + + return ( + <> + +
    + {files.map((file, index) => ( +
  • + {file.name} + {' '} + +
  • + ))} +
+ + ); +}; + +FileUploader.propTypes = { + onChange: PropTypes.func.isRequired, + files: PropTypes.arrayOf(PropTypes.instanceOf(File)), +}; + +FileUploader.defaultProps = { + files: [], +}; + +export default FileUploader; diff --git a/frontend/src/components/MultiSelect.css b/frontend/src/components/MultiSelect.css new file mode 100644 index 0000000000..2f6f0840f9 --- /dev/null +++ b/frontend/src/components/MultiSelect.css @@ -0,0 +1,9 @@ +.smart-hub--select-icon { + background-image: url("../images/arrow-both.svg"); + background-size: 0.5rem; + padding-right: 2rem; + background-position: right 0.4rem center; + background-repeat: no-repeat; + background-color: white; + height: 22px; +} diff --git a/frontend/src/components/MultiSelect.js b/frontend/src/components/MultiSelect.js new file mode 100644 index 0000000000..84f39a9681 --- /dev/null +++ b/frontend/src/components/MultiSelect.js @@ -0,0 +1,70 @@ +/* + +*/ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Label } from '@trussworks/react-uswds'; +import { Controller } from 'react-hook-form'; +import Select, { components } from 'react-select'; + +import './MultiSelect.css'; + +const DropdownIndicator = (props) => ( + // eslint-disable-next-line react/jsx-props-no-spreading + +
+ +); + +const MultiSelect = ({ + disabled, control, options, name, label, placeholder, +}) => ( + <> + + ( +