diff --git a/.circleci/config.yml b/.circleci/config.yml
index b912533503..4bc7b6d8ca 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -133,7 +133,7 @@ parameters:
default: "main"
type: string
sandbox_git_branch: # change to feature branch to test deployment
- default: "js-117-selection-of-goals"
+ default: "js-6-frontend-validations"
type: string
jobs:
build_and_lint:
diff --git a/frontend/package.json b/frontend/package.json
index 9e746df00d..422e0b5e12 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -8,6 +8,7 @@
"@fortawesome/fontawesome-svg-core": "^1.2.32",
"@fortawesome/free-solid-svg-icons": "^5.15.1",
"@fortawesome/react-fontawesome": "^0.1.11",
+ "@hookform/error-message": "^0.0.5",
"@testing-library/jest-dom": "^4.2.4",
"@trussworks/react-uswds": "^1.9.1",
"@use-it/interval": "^1.0.0",
@@ -21,7 +22,7 @@
"react-dom": "^16.14.0",
"react-dropzone": "^11.2.0",
"react-helmet": "^6.1.0",
- "react-hook-form": "^6.9.0",
+ "react-hook-form": "^6.15.0",
"react-idle-timer": "^4.4.2",
"react-input-autosize": "^3.0.0",
"react-responsive": "^8.1.1",
diff --git a/frontend/src/components/DatePicker.css b/frontend/src/components/DatePicker.css
index 6539117efa..1586359b58 100644
--- a/frontend/src/components/DatePicker.css
+++ b/frontend/src/components/DatePicker.css
@@ -14,3 +14,9 @@
.DateInput {
width: fit-content;
}
+
+.usa-hint {
+ font-size: 14px;
+ margin-top: 5px;
+ margin-bottom: 10px;
+}
\ No newline at end of file
diff --git a/frontend/src/components/DatePicker.js b/frontend/src/components/DatePicker.js
index beab959f6c..1863257cac 100644
--- a/frontend/src/components/DatePicker.js
+++ b/frontend/src/components/DatePicker.js
@@ -10,10 +10,8 @@
2. react-dates had easily readable documentation and conveniences such as `maxDate`
and `minDate`. I couldn't find great docs using the USWDS datepicker javascript
*/
-
import React, { useState } from 'react';
import PropTypes from 'prop-types';
-import { Label } from '@trussworks/react-uswds';
import { SingleDatePicker } from 'react-dates';
import { OPEN_UP, OPEN_DOWN } from 'react-dates/constants';
import { Controller } from 'react-hook-form';
@@ -24,9 +22,8 @@ import './DatePicker.css';
const dateFmt = 'MM/DD/YYYY';
const DateInput = ({
- control, label, minDate, name, disabled, maxDate, openUp, required,
+ control, minDate, name, disabled, maxDate, openUp, required,
}) => {
- const labelId = `${name}-id`;
const hintId = `${name}-hint`;
const [isFocused, updateFocus] = useState(false);
const openDirection = openUp ? OPEN_UP : OPEN_DOWN;
@@ -40,8 +37,7 @@ const DateInput = ({
return (
<>
-
-
mm/dd/yyyy
+ mm/dd/yyyy
{
const date = value ? moment(value, dateFmt) : null;
@@ -57,7 +53,10 @@ const DateInput = ({
numberOfMonths={1}
openDirection={openDirection}
disabled={disabled}
- onDateChange={(d) => { onChange(d.format(dateFmt)); }}
+ onDateChange={(d) => {
+ const newDate = d ? d.format(dateFmt) : d;
+ onChange(newDate);
+ }}
onFocusChange={({ focused }) => updateFocus(focused)}
/>
@@ -68,7 +67,7 @@ const DateInput = ({
disabled={disabled}
defaultValue={null}
rules={{
- required,
+ required: required ? 'Please select a date' : false,
}}
/>
>
@@ -80,7 +79,6 @@ DateInput.propTypes = {
// eslint-disable-next-line react/forbid-prop-types
control: PropTypes.object.isRequired,
name: PropTypes.string.isRequired,
- label: PropTypes.string.isRequired,
minDate: PropTypes.string,
maxDate: PropTypes.string,
openUp: PropTypes.bool,
diff --git a/frontend/src/components/FormItem.css b/frontend/src/components/FormItem.css
new file mode 100644
index 0000000000..f2ddc3bd1e
--- /dev/null
+++ b/frontend/src/components/FormItem.css
@@ -0,0 +1,5 @@
+.smart-hub--form-required {
+ font-family: SourceSansPro;
+ font-size: 16px;
+ color: #d42240;
+}
diff --git a/frontend/src/components/FormItem.js b/frontend/src/components/FormItem.js
new file mode 100644
index 0000000000..672d74268a
--- /dev/null
+++ b/frontend/src/components/FormItem.js
@@ -0,0 +1,79 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { useFormContext } from 'react-hook-form';
+import { ErrorMessage as ReactHookFormError } from '@hookform/error-message';
+import {
+ Label, FormGroup, ErrorMessage, Fieldset,
+} from '@trussworks/react-uswds';
+
+import './FormItem.css';
+
+const labelPropTypes = {
+ label: PropTypes.node.isRequired,
+ children: PropTypes.node.isRequired,
+};
+
+function Checkbox({ label, children }) {
+ return (
+
+ );
+}
+
+Checkbox.propTypes = labelPropTypes;
+
+function Field({ label, children }) {
+ return (
+
+ );
+}
+
+Field.propTypes = labelPropTypes;
+
+function FormItem({
+ label, children, required, name, isCheckbox,
+}) {
+ const { formState: { errors } } = useFormContext();
+ const fieldErrors = errors[name];
+ const labelWithRequiredTag = (
+ <>
+ {label}
+ {required && ( (Required))}
+ >
+ );
+
+ const LabelType = isCheckbox ? Checkbox : Field;
+
+ return (
+
+
+ {message}}
+ />
+ {children}
+
+
+ );
+}
+
+FormItem.propTypes = {
+ label: PropTypes.string.isRequired,
+ children: PropTypes.node.isRequired,
+ name: PropTypes.string.isRequired,
+ isCheckbox: PropTypes.bool,
+ required: PropTypes.bool,
+};
+
+FormItem.defaultProps = {
+ required: true,
+ isCheckbox: false,
+};
+
+export default FormItem;
diff --git a/frontend/src/components/MultiSelect.js b/frontend/src/components/MultiSelect.js
index 68c79d3717..ce008e2c47 100644
--- a/frontend/src/components/MultiSelect.js
+++ b/frontend/src/components/MultiSelect.js
@@ -23,7 +23,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import Select, { components } from 'react-select';
-import { Label } from '@trussworks/react-uswds';
import { Controller } from 'react-hook-form';
import arrowBoth from '../images/arrow-both.svg';
@@ -73,7 +72,6 @@ const styles = {
};
function MultiSelect({
- label,
name,
options,
disabled,
@@ -117,44 +115,45 @@ function MultiSelect({
};
return (
-
+ {
+ const values = value ? getValues(value) : value;
+ return (
+