Skip to content
This repository was archived by the owner on Dec 1, 2025. It is now read-only.

Commit 30c941d

Browse files
authored
Merge pull request #130 from thepensionsregulator/feature-forms-email-input
Feature forms email input
2 parents b25a1f7 + 5196ed8 commit 30c941d

26 files changed

Lines changed: 563 additions & 46 deletions

File tree

packages/forms/src/__mocks__/setup.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,7 @@ export function formSetup({
3434
</Form>,
3535
);
3636

37+
// console.log(renderArg);
38+
3739
return { ...renderArg, ...utils };
3840
}

packages/forms/src/__tests__/checkbox.spec.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { renderFields } from '../renderFields';
55
import { fireEvent } from '@testing-library/react';
66
import { axe } from 'jest-axe';
77

8-
describe('Checkbox', () => {
8+
describe('Checkbox input', () => {
99
test('is accessible', async () => {
1010
const handleSubmit = jest.fn();
1111
const { container } = formSetup({

packages/forms/src/__tests__/date.spec.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { axe } from 'jest-axe';
66

77
// TODO: write more test when there are clear specs for date input validation
88

9-
describe('Date', () => {
9+
describe('Date input', () => {
1010
test('is accessible', async () => {
1111
const fields: FieldProps[] = [
1212
{
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import React from 'react';
2+
import { formSetup } from '../__mocks__/setup';
3+
import { FFInputEmail } from '../elements/email/email';
4+
import { axe } from 'jest-axe';
5+
import userEvent from '@testing-library/user-event';
6+
7+
describe('Email input', () => {
8+
test('is accessible', async () => {
9+
const { container } = formSetup({
10+
render: <FFInputEmail label="Email" testId="email-input" name="email" />,
11+
});
12+
const results = await axe(container);
13+
expect(results).toHaveNoViolations();
14+
});
15+
16+
test('values get captured correctly', async () => {
17+
const testId = 'email-input';
18+
const handleSubmit = jest.fn();
19+
const { getByText, getByTestId } = formSetup({
20+
render: <FFInputEmail label="Email" testId={testId} name="email" />,
21+
onSubmit: handleSubmit,
22+
});
23+
24+
userEvent.type(getByTestId(testId), 'david.alekna@tpr.gov.uk');
25+
getByText('Submit').click();
26+
27+
expect(getByTestId(testId)).toHaveValue('david.alekna@tpr.gov.uk');
28+
});
29+
30+
test('should not accept invalid email addresses', async () => {
31+
const testId = 'email-input';
32+
const handleSubmit = jest.fn();
33+
const { getByText, getByTestId, form } = formSetup({
34+
render: <FFInputEmail label="Email" testId={testId} name="email" />,
35+
onSubmit: handleSubmit,
36+
});
37+
38+
userEvent.type(getByTestId(testId), 'this is not an email address');
39+
getByText('Submit').click();
40+
41+
expect(form.getState().valid).toBeFalsy();
42+
});
43+
44+
test('accepts only valid emails', async () => {
45+
const testId = 'email-input';
46+
const handleSubmit = jest.fn();
47+
const { getByText, getByTestId, form } = formSetup({
48+
render: <FFInputEmail label="Email" testId={testId} name="email" />,
49+
onSubmit: handleSubmit,
50+
});
51+
52+
userEvent.type(getByTestId(testId), 'david.alekna@tpr.gov.uk');
53+
getByText('Submit').click();
54+
55+
expect(form.getState().valid).toBeTruthy();
56+
});
57+
58+
test('composes custom validation function', async () => {
59+
const testId = 'email-input';
60+
const errorMessage = 'Must be a TPR email';
61+
const handleSubmit = jest.fn();
62+
const { getByText, getByTestId, queryByText, form } = formSetup({
63+
render: (
64+
<FFInputEmail
65+
label="Email"
66+
testId={testId}
67+
name="email"
68+
validate={(email) =>
69+
email && email.includes('tpr.gov.uk') ? undefined : errorMessage
70+
}
71+
/>
72+
),
73+
onSubmit: handleSubmit,
74+
});
75+
76+
userEvent.type(getByTestId(testId), 'david.alekna@gmail.com');
77+
getByText('Submit').click();
78+
79+
expect(queryByText(errorMessage)).toBeInTheDocument();
80+
expect(form.getState().valid).toBeFalsy();
81+
});
82+
});

packages/forms/src/__tests__/form.spec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ describe('Form', () => {
2323
const handleSubmit = jest.fn();
2424
const { container, form } = formSetup({
2525
render: renderFields([
26-
{ name: 'firstName', type: 'text' },
27-
{ name: 'lastName', type: 'text' },
28-
{ name: 'email', type: 'email' },
26+
{ label: 'First Name', name: 'firstName', type: 'text' },
27+
{ label: 'Last Name', name: 'lastName', type: 'text' },
28+
{ label: 'Email', name: 'email', type: 'email' },
2929
]),
3030
onSubmit: handleSubmit,
3131
initialValues: {},
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import React from 'react';
2+
import { formSetup } from '../__mocks__/setup';
3+
import { FFInputPhone } from '../elements/phone/phone';
4+
import { axe } from 'jest-axe';
5+
import userEvent from '@testing-library/user-event';
6+
7+
describe('Phone input', () => {
8+
test('is accessible', async () => {
9+
const { container } = formSetup({
10+
render: (
11+
<FFInputPhone label="Phone number" testId="phone-input" name="phone" />
12+
),
13+
});
14+
const results = await axe(container);
15+
expect(results).toHaveNoViolations();
16+
});
17+
18+
test('values get captured correctly', async () => {
19+
const testId = 'phone-input';
20+
const handleSubmit = jest.fn();
21+
const { getByText, getByTestId } = formSetup({
22+
render: (
23+
<FFInputPhone label="Phone number" testId={testId} name="phone" />
24+
),
25+
onSubmit: handleSubmit,
26+
});
27+
28+
userEvent.type(getByTestId(testId), '07543 221 321');
29+
getByText('Submit').click();
30+
31+
expect(getByTestId(testId)).toHaveValue('07543 221 321');
32+
});
33+
34+
test('should not accept invalid numbers', async () => {
35+
const testId = 'phone-input';
36+
const handleSubmit = jest.fn();
37+
const { getByText, getByTestId, form } = formSetup({
38+
render: (
39+
<FFInputPhone label="Phone number" testId={testId} name="phone" />
40+
),
41+
onSubmit: handleSubmit,
42+
});
43+
44+
userEvent.type(
45+
getByTestId(testId),
46+
'this is not a valid phone number address',
47+
);
48+
getByText('Submit').click();
49+
50+
expect(form.getState().valid).toBeFalsy();
51+
expect(getByText('Invalid phone number')).toBeInTheDocument();
52+
});
53+
54+
test('accepts only valid numbers', async () => {
55+
const testId = 'phone-input';
56+
const handleSubmit = jest.fn();
57+
const { getByText, getByTestId, form } = formSetup({
58+
render: (
59+
<FFInputPhone label="Phone number" testId={testId} name="phone" />
60+
),
61+
onSubmit: handleSubmit,
62+
});
63+
64+
userEvent.type(getByTestId(testId), '07543 221 321');
65+
getByText('Submit').click();
66+
67+
expect(form.getState().valid).toBeTruthy();
68+
});
69+
70+
test('composes custom validation function', async () => {
71+
const testId = 'phone-input';
72+
const errorMessage = 'Must include tripple 7';
73+
const handleSubmit = jest.fn();
74+
const { getByText, getByTestId, queryByText, form } = formSetup({
75+
render: (
76+
<FFInputPhone
77+
label="Phone number"
78+
testId={testId}
79+
name="phone"
80+
validate={(phone) =>
81+
phone && phone.includes('777') ? undefined : errorMessage
82+
}
83+
/>
84+
),
85+
onSubmit: handleSubmit,
86+
});
87+
88+
userEvent.type(getByTestId(testId), '07543 221 321');
89+
getByText('Submit').click();
90+
91+
expect(queryByText(errorMessage)).toBeInTheDocument();
92+
expect(form.getState().valid).toBeFalsy();
93+
});
94+
});

packages/forms/src/__tests__/radio.spec.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { renderFields } from '../index';
55
import { fireEvent } from '@testing-library/react';
66
import { axe } from 'jest-axe';
77

8-
describe('RadioButton', () => {
8+
describe('Radio input', () => {
99
test('is accessible', async () => {
1010
const handleSubmit = jest.fn();
1111
const { container } = formSetup({

packages/forms/src/__tests__/select.spec.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { formSetup } from '../__mocks__/setup';
33
import { FFSelect } from '../elements/select/select';
44
import { axe } from 'jest-axe';
55

6-
describe('Select', () => {
6+
describe('Select input', () => {
77
test('is accessible', async () => {
88
const testId = 'select-input';
99
const items = [

packages/forms/src/__tests__/text.spec.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { validate, renderFields } from '../index';
66
import { FieldProps } from '../renderFields';
77
import { axe } from 'jest-axe';
88

9-
describe('Text', () => {
9+
describe('Text input', () => {
1010
test('is accessible', async () => {
1111
const { container } = formSetup({
1212
render: (
@@ -80,7 +80,7 @@ describe('Text', () => {
8080
test('handles input', async () => {
8181
const handleSubmit = jest.fn();
8282
const { container, form } = formSetup({
83-
render: <FFInputText name="name" type="text" />,
83+
render: <FFInputText label="Input Text" name="name" type="text" />,
8484
onSubmit: handleSubmit,
8585
});
8686
const name = container.querySelector('input[name="name"]');
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
---
2+
name: Email
3+
menu: Forms
4+
route: /forms/email
5+
---
6+
7+
import { Playground } from '@playground';
8+
import { Form } from 'react-final-form';
9+
import { FFInputEmail } from './email';
10+
import { validate } from '../../validation';
11+
import { renderFields } from '../../renderFields';
12+
13+
# Input
14+
15+
A basic widget for getting the user input as a email field. Keyboard and mouse can be used for providing or changing data.
16+
17+
## When To Use
18+
19+
- A user input in a form field is needed.
20+
- A search input is required.
21+
22+
## Usage
23+
24+
`Inside your React project directory, run the following:`
25+
26+
```js
27+
yarn add @tpr/forms
28+
```
29+
30+
`or with npm`
31+
32+
```js
33+
npm install @tpr/forms
34+
```
35+
36+
`import items you wish to use from the library`
37+
38+
```js
39+
import { Form, FFInputEmail } from '@tpr/forms';
40+
```
41+
42+
## Examples
43+
44+
[CodeSandbox](https://codesandbox.io)
45+
46+
Example using field level validation
47+
48+
<Playground>
49+
<Form onSubmit={console.log}>
50+
{({ handleSubmit }) => (
51+
<form onSubmit={handleSubmit}>
52+
<FFInputEmail
53+
name="event_name"
54+
label="Event name"
55+
placeholder="john.smith@tpr.gov.uk"
56+
hint="Must be between 6 and 8 digits long"
57+
validate={(email) =>
58+
email && email.includes('tpr.gov.uk')
59+
? undefined
60+
: 'Must be a TPR email'
61+
}
62+
inputWidth={5}
63+
cfg={{ my: 5 }}
64+
/>
65+
<button type="submit" style={{ display: 'none' }} children="Submit" />
66+
</form>
67+
)}
68+
</Form>
69+
</Playground>
70+
71+
## API
72+
73+
```
74+
Accepted config props: FlexProps, SpaceProps
75+
```
76+
77+
### Props
78+
79+
| Property | Required | Type | Description |
80+
| -------- | -------- | ------- | -------------------------------------------- |
81+
| cfg | false | object | FlexProps & SpaceProps |
82+
| disabled | false | boolean | Disable checkbox |
83+
| testId | false | string | data attribute for testers |
84+
| label | true | string | Checkbox description |
85+
| hint | false | string | More detailed description about the checkbox |

0 commit comments

Comments
 (0)