diff --git a/f2/src/components/ErrorSummary/__tests__/ErrorSummary.test.js b/f2/src/components/ErrorSummary/__tests__/ErrorSummary.test.js
new file mode 100644
index 000000000..e260f45a7
--- /dev/null
+++ b/f2/src/components/ErrorSummary/__tests__/ErrorSummary.test.js
@@ -0,0 +1,113 @@
+import React from 'react'
+import { ThemeProvider } from 'emotion-theming'
+import wait from 'waait'
+import canada from '../../../theme/canada'
+import { render, fireEvent, cleanup } from '@testing-library/react'
+import { ErrorSummary } from '../'
+import { Form } from 'react-final-form'
+import { Field } from '../../Field'
+import { I18nProvider } from '@lingui/react'
+import { i18n } from '@lingui/core'
+import en from '../../../locales/en.json'
+
+i18n.load('en', { en })
+i18n.activate('en')
+const clickOn = element => fireEvent.click(element)
+
+describe('', () => {
+ afterEach(cleanup)
+
+ it('does not render if validation passes', () => {
+ const submitMock = jest.fn()
+
+ const validate = values => {
+ const errors = {}
+ //condition for an error to occur: append a lingui id to the list of error
+ if (!values.foo) {
+ errors.foo = 'bar'
+ }
+ return errors
+ }
+
+ const { queryByText } = render(
+
+
+
+ )}
+ />
+
+ ,
+ )
+ expect(queryByText(/bar/)).toBeNull()
+ })
+
+ it('displays an error summary that links to a error message', async () => {
+ const submitMock = jest.fn()
+
+ const validate = values => {
+ const errors = {}
+ //condition for an error to occur: append a lingui id to the list of error
+ if (values.foo === 'baz') {
+ errors.foo = 'bar'
+ }
+ return errors
+ }
+
+ const { queryAllByText, getByText } = render(
+
+
+
+ )}
+ />
+
+ ,
+ )
+
+ //Errors should display on submit. At first expect to have no error message
+ expect(queryAllByText(/bar/)).toHaveLength(0)
+ await wait(0) // Wait for promises to resolve
+
+ //get the submit button, then click on it. Error summary renders only on submit
+ const context = document.querySelector('[type="submit"]').textContent
+ const submitButton = getByText(context)
+ clickOn(submitButton)
+ await wait(0) // Wait for promises to resolve
+
+ expect(queryAllByText(/bar/)).toHaveLength(2)
+ })
+})
diff --git a/f2/src/components/ErrorSummary/index.js b/f2/src/components/ErrorSummary/index.js
new file mode 100644
index 000000000..ae0c87e87
--- /dev/null
+++ b/f2/src/components/ErrorSummary/index.js
@@ -0,0 +1,51 @@
+/** @jsx jsx **/
+import { jsx } from '@emotion/core'
+import { useLingui } from '@lingui/react'
+import { Trans } from '@lingui/macro'
+import { Stack } from '@chakra-ui/core'
+import { Text } from '../text'
+import { Ol } from '../ordered-list'
+import { Li } from '../list-item'
+import { A } from '../link'
+import { useForm } from 'react-final-form'
+import { Alert } from '../Messages'
+import { useEffect } from 'react'
+
+export const ErrorSummary = props => {
+ const { i18n } = useLingui()
+
+ const { errors } = useForm(props.onSubmit).getState()
+
+ useEffect(() => {
+ const summary = document
+ .getElementById('error-summary')
+ .getBoundingClientRect()
+
+ window.scrollTo(0, summary.y - 16)
+ })
+
+ return (
+
+
+
+
+
+
+
+ {Object.keys(errors).map(key => (
+ -
+
+ {i18n._(errors[key])}
+
+
+ ))}
+
+
+
+ )
+}
diff --git a/f2/src/components/Field/index.js b/f2/src/components/Field/index.js
index a2258e74e..f34fcab23 100644
--- a/f2/src/components/Field/index.js
+++ b/f2/src/components/Field/index.js
@@ -1,10 +1,11 @@
/** @jsx jsx */
import { jsx } from '@emotion/core'
import PropTypes from 'prop-types'
-import { FormErrorMessage, FormControl } from '@chakra-ui/core'
+import { FormControl } from '@chakra-ui/core'
import { FormHelperText } from '../FormHelperText'
import { FormLabel } from '../FormLabel'
-import { Field as FieldAdapter } from 'react-final-form'
+import { FormErrorMessage } from '../FormErrorMessage'
+import { Field as FieldAdapter, useField } from 'react-final-form'
import { UniqueID } from '../unique-id'
import { Input } from '../input'
@@ -16,16 +17,31 @@ export const Field = ({
component,
...props
}) => {
+ const {
+ meta: { invalid, submitFailed },
+ } = useField(name, {
+ subscription: {
+ invalid: true,
+ submitFailed: true,
+ },
+ })
+
return (
{id => {
return (
-
+
{label}
{helperText && {helperText}}
- {errorMessage}
+ {errorMessage && (
+ {errorMessage}
+ )}
{
const {
- meta: { error, touched },
- } = useField(name, { subscription: { touched: true, error: true } })
+ meta: { submitFailed, invalid },
+ } = useField(name, {
+ subscription: { submitFailed: true, invalid: true },
+ })
+
return (
{id => {
return (
-
- {label}
-
- {helperText}
- {errorMessage}
+
+
+ {label}
+
+ {helperText && {helperText}}
+ {errorMessage && (
+ {errorMessage}
+ )}
+
+
{/** This component comes with a group attribute. We don't need to use Chakra's or as per the Chakra docs */}
{children}
diff --git a/f2/src/components/FormErrorMessage/index.js b/f2/src/components/FormErrorMessage/index.js
new file mode 100644
index 000000000..3c88ee2cd
--- /dev/null
+++ b/f2/src/components/FormErrorMessage/index.js
@@ -0,0 +1,20 @@
+import React from 'react'
+import { Text, useFormControl } from '@chakra-ui/core'
+
+export const FormErrorMessage = props => {
+ const formControl = useFormControl(props)
+ if (!formControl.isInvalid) {
+ return null
+ }
+ return {props.children}
+}
+
+FormErrorMessage.defaultProps = {
+ fontSize: 'md',
+ fontWeight: 'bold',
+ color: 'red.700',
+ fontFamily: 'body',
+ lineHeight: 1.25,
+ mb: 1,
+ maxW: '600px',
+}
diff --git a/f2/src/components/FormHelperText/index.js b/f2/src/components/FormHelperText/index.js
index 7bb1a8320..4ef7f2a6e 100644
--- a/f2/src/components/FormHelperText/index.js
+++ b/f2/src/components/FormHelperText/index.js
@@ -1,28 +1,11 @@
import styled from '@emotion/styled'
import { FormHelperText as ChakraFormHelperText } from '@chakra-ui/core'
-import { variant } from 'styled-system'
-export const FormHelperText = styled(ChakraFormHelperText)(
- variant({
- variants: {
- above: {
- mt: -2,
- mb: 4,
- },
- below: {
- mt: 4,
- mb: 0,
- },
- unstyled: {
- m: 0,
- },
- },
- }),
-)
+export const FormHelperText = styled(ChakraFormHelperText)()
FormHelperText.defaultProps = {
- variant: 'above',
as: 'p',
+ mt: 0,
fontSize: 'md',
color: 'black',
fontFamily: 'body',
diff --git a/f2/src/components/FormLabel/index.js b/f2/src/components/FormLabel/index.js
index 7a498ff04..53135ac4e 100644
--- a/f2/src/components/FormLabel/index.js
+++ b/f2/src/components/FormLabel/index.js
@@ -7,7 +7,7 @@ FormLabel.defaultProps = {
fontSize: 'xl',
fontWeight: 'bold',
fontFamily: 'body',
- mb: 2,
+ p: 0,
lineHeight: 1,
maxW: '600px',
}
diff --git a/f2/src/components/input/index.js b/f2/src/components/input/index.js
index d8eff1fd2..41066c035 100644
--- a/f2/src/components/input/index.js
+++ b/f2/src/components/input/index.js
@@ -8,6 +8,9 @@ export const Input = props => (
onKeyPress={e => {
e.key === 'Enter' && e.preventDefault()
}}
+ _invalid={{
+ borderColor: 'red.700',
+ }}
autoComplete="off"
{...canada.variants.inputs.inputs}
{...props.input}
diff --git a/f2/src/forms/PrivacyConsentInfoForm.js b/f2/src/forms/PrivacyConsentInfoForm.js
index b75e0b47b..69b825f7f 100644
--- a/f2/src/forms/PrivacyConsentInfoForm.js
+++ b/f2/src/forms/PrivacyConsentInfoForm.js
@@ -1,25 +1,24 @@
import React from 'react'
import PropTypes from 'prop-types'
import { Trans } from '@lingui/macro'
-import { Form, useField } from 'react-final-form'
+import { Form } from 'react-final-form'
import { NextAndCancelButtons } from '../components/next-and-cancel-buttons'
-import { FormControl, Stack } from '@chakra-ui/core'
+import { Stack } from '@chakra-ui/core'
import { useStateValue } from '../utils/state'
import { A } from '../components/link'
import { CheckboxAdapter } from '../components/checkbox'
import { FormArrayControl } from '../components/FormArrayControl'
import { useLingui } from '@lingui/react'
-import { Alert } from '../components/Messages'
+import { ErrorSummary } from '../components/ErrorSummary'
-const Control = ({ name, ...rest }) => {
- const {
- meta: { error, touched },
- } = useField(name, { subscription: { touched: true, error: true } })
- return
-}
+const validate = values => {
+ const errors = {}
+ //condition for an error to occur: append a lingui id to the list of error
+ if (!values.consentOptions || values.consentOptions.length < 1) {
+ errors.consentOptions = 'privacyConsentInfoForm.warning'
+ }
-const validate = () => {
- return {}
+ return errors
}
export const PrivacyConsentInfoForm = props => {
@@ -30,7 +29,6 @@ export const PrivacyConsentInfoForm = props => {
...data.formData.whetherConsent,
}
const consentOptions = ['privacyConsentInfoForm.yes']
- let showWarning = false
return (
@@ -43,53 +41,51 @@ export const PrivacyConsentInfoForm = props => {