Skip to content
This repository was archived by the owner on Feb 27, 2024. It is now read-only.

Commit 53ec459

Browse files
committed
Merge remote-tracking branch 'origin/fix/cleanup' into fix/cleanup
# Conflicts: # pages/[...slug].js
2 parents 7063e3d + 4e90ea2 commit 53ec459

File tree

12 files changed

+639
-28
lines changed

12 files changed

+639
-28
lines changed

components/molecules/Form/Form.module.css

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
.form {
2+
@apply bg-white text-black;
3+
24
padding: 1em;
35

46
& .fields {
@@ -13,5 +15,6 @@
1315
@apply block relative;
1416

1517
color: black;
18+
outline: 1px solid black;
1619
}
1720
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import {useState} from 'react'
2+
import {Meta, Story, Canvas} from '@storybook/addon-docs/blocks'
3+
import noop from 'lodash/noop'
4+
import {Field, ErrorMessage} from 'formik'
5+
import * as Yup from 'yup'
6+
7+
import Form from '.'
8+
9+
<Meta title="Design System/Molecules/Form" component={Form} />
10+
11+
# Form
12+
13+
Use this component to render a form.
14+
15+
<Canvas>
16+
<Story name="Component">
17+
<Form
18+
className="sample-form"
19+
id="form-1"
20+
formDefaults={{
21+
firstName: 'test'
22+
}}
23+
title="Sample Form"
24+
validationSchema={Yup.object().shape({
25+
firstName: Yup.string()
26+
.required('This field is required.')
27+
.min(5, 'Minimum of 5 characters.')
28+
})}
29+
>
30+
<label htmlFor="firstName">First Name</label>
31+
<Field id="firstName" name="firstName" placeholder="Jane" type="text" />
32+
<ErrorMessage name="firstName" />
33+
</Form>
34+
</Story>
35+
</Canvas>
36+
37+
## How it Works
38+
39+
The `Form` component under the hood is powered by [Formik](https://formik.org/docs/overview). Formik is used for form state management, validation, and form submissions.
40+
41+
## Setting up form defaults and state
42+
43+
The `formDefaults` handles your data by passing it to `Formik` as `initialValues`. Each property passed into `formDefaults` should match the name of the input it corresponds to. The name prop is how Formik manages the state of each input.
44+
45+
## Creating an Input
46+
47+
Creating form inputs with Formik can be as simple or custom as you would like.
48+
49+
_The following example creates a basic text input field using the Formik `<Field />` component. The `name` prop on the `field` is set to `firstName` which is used for tracking the import state._
50+
51+
```js
52+
<label htmlFor="firstName">First Name</label>
53+
<Field id="firstName" name="firstName" placeholder="Jane" />
54+
```
55+
56+
**Example Default**
57+
58+
The following example shows a property called `firstName` that has a default value of an empty string. Default values should be included for each field.
59+
60+
```js
61+
const formDefaults = {
62+
firstName: ''
63+
}
64+
```
65+
66+
### Field validation and error handling
67+
68+
The Form component relies on Formik and Yup to validate inputs and display input errors. Formik is in charge of displaying the error and processing validation. Meanwhile, Yup handles validation similar to how PropTypes work. This feature can be utilized by creating a validation object and passing it into `validationSchema`.
69+
70+
**Setting up input validationSchema**
71+
72+
The following example demonstrates adding input validaton to the `firstName` text input. By passing in the name of the input and a schema validation Object, this input will automatically be validated according to the schema Object.
73+
74+
```js
75+
{
76+
firstName: Yup.string().required()
77+
}
78+
```
79+
80+
**Displaying input errors**
81+
82+
To display an error on an input use the [Formik `<ErrorMessage />` component](https://formik.org/docs/api/errormessage). This component takes in a name prop that should correspond to the input name it's displaying an error for.
83+
84+
This example, will now render the error text for `firstName` from the validationSchema.
85+
86+
```js
87+
<ErrorMessage name="firstName" />
88+
```
89+
90+
### Form Submission
91+
92+
Needs documentation...
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
import {useState} from 'react'
2+
import {Meta, Story, Canvas} from '@storybook/addon-docs/blocks'
3+
import noop from 'lodash/noop'
4+
import Fields from './Fields'
5+
6+
export const fieldsSample = [
7+
{
8+
__typename: 'GravityFormsFormToObjectFieldUnionConnectionEdge',
9+
node: {
10+
__typename: 'CheckboxField',
11+
id: 2,
12+
adminLabel: '',
13+
adminOnly: null,
14+
allowsPrepopulate: false,
15+
conditionalLogic: {
16+
__typename: 'ConditionalLogic',
17+
rules: null,
18+
actionType: null,
19+
logicType: null
20+
},
21+
cssClassList: [],
22+
label: 'Checkbox field example 2.',
23+
type: 'checkbox',
24+
visibility: 'visible',
25+
checkboxChoices: [
26+
{
27+
__typename: 'ChoiceProperty',
28+
isSelected: false,
29+
text: 'New Choice 1',
30+
value: 'New Choice 1'
31+
},
32+
{
33+
__typename: 'ChoiceProperty',
34+
isSelected: false,
35+
text: 'New Choice 2',
36+
value: 'New Choice 2'
37+
},
38+
{
39+
__typename: 'ChoiceProperty',
40+
isSelected: false,
41+
text: 'New Choice 3',
42+
value: 'New Choice 3'
43+
}
44+
],
45+
description: 'This field is required.',
46+
enableChoiceValue: null,
47+
enableSelectAll: true,
48+
errorMessage: '',
49+
inputName: '',
50+
inputs: [
51+
{
52+
__typename: 'CheckboxInputProperty',
53+
id: 2.1,
54+
label: 'New Choice 1',
55+
name: ''
56+
},
57+
{
58+
__typename: 'CheckboxInputProperty',
59+
id: 2.2,
60+
label: 'New Choice 2',
61+
name: ''
62+
},
63+
{
64+
__typename: 'CheckboxInputProperty',
65+
id: 2.3,
66+
label: 'New Choice 3',
67+
name: ''
68+
}
69+
],
70+
isRequired: true,
71+
size: 'medium'
72+
}
73+
}
74+
]
75+
76+
<Meta title="Design System/Molecules/GravityForm/Fields" component={Fields} />
77+
78+
# Fields
79+
80+
The `<Fields />` component handles maps through each GravityForm field in the form and matches it up with a GravityForm field component.
81+
82+
_Note: This component should not be used outside of the `<GravityForm />._
83+
84+
## Adding a new field
85+
86+
To add a new form field do the following:
87+
88+
1. Create a test form in the GravityForm UI.
89+
2. Add the field that you would like to add support for.
90+
3. Add the form to a test page that renders the GravityForm block.
91+
4. Next, locate the Field directory `components/molecules/GravityForm/Fields/` and create a new component file such as `Text.js`. Note: This component name should matchup with the field name from GravityForms, not the input name.
92+
5. In `Fields/Fields.js` add a new case to the switch statement for your field type. All GravityForm fields should have a `type` property. You can reference your component by using dot notation ex:`GfFields.Text`.
93+
6. Check that the new Field component renders on your test page from step 3.
94+
95+
**Things to Know**
96+
97+
- Input components should be created in `components/atoms/Inputs/` directory.
98+
- GravityForm Field components should map props to the inputs.
99+
- Files names should correspond to the field type.
100+
101+
## Setting up Field Defaults
102+
103+
Needs documentation...
104+
105+
## Adding field validation
106+
107+
Validation can be added to each GravityForm field type by generating a validationSchema Object, or enhancing an already existing SchemaFactory.
108+
109+
Field validation for GravityForms occurs during the field mapping process in `<Fields />` component. Each field type is matched up with [Yup validation](https://github.com/jquense/yup#api) type. If a match is found it will be processed using a `SchemaFactory` which will return a field validationSchema Object.
110+
111+
Note: Validation types cannot be mixed.
112+
113+
### Updating existing validation types
114+
115+
To update an existing validation type, locate the `SchemaFactory` for that field type. If there is not a match you'll need to create a new `SchemaFactories` for that validation type.
116+
117+
_`SchemaFactories` are located in `functions/gravityForms/yupSchema`._
118+
119+
2. To edit an existing validation, locate the method defined in `SchemaFactory`.
120+
3. To add a new validation method, do the following:
121+
4. Add new validation method.
122+
5. Include the new method in `schema()` using `.concat(this.yourNewMethod)`.
123+
124+
### New Validation Types
125+
126+
To add a new type of validation do the following:
127+
128+
1. Add a new case `getValidationSchemaByType()` for the field type.
129+
2. Clone an existing `SchemaFactory` and update it with new methods and references to Yup API ex: `Yup.string()` to `Yup.number()` etc...
130+
131+
## Unsupported Fields
132+
133+
Due to the amount of available GravityForm fields support will need to be added as needed.

components/molecules/GravityForm/Fields/Fields.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,15 @@ import * as GfFields from '.'
33
import {useEffect} from 'react'
44
import getGfFieldValidationSchema from '@/functions/gravityForms/getGfFieldValidationSchema'
55

6+
/**
7+
* Render the Fields component.
8+
*
9+
* @author WebDevStudios
10+
* @param {object} props The component attributes as props.
11+
* @param {Array} props.fields GravityForm fields data.
12+
* @param {Function} props.setFormValidation Callback function for setting formValidation state.
13+
* @return {Element} The Fields component.
14+
*/
615
export default function Fields({fields, setFormValidation}) {
716
/**
817
* Map through fields to setup form validation.
@@ -34,10 +43,22 @@ export default function Fields({fields, setFormValidation}) {
3443
fieldToRender = <GfFields.Checkbox {...field.node} key={id} />
3544
break
3645

46+
case 'email':
47+
fieldToRender = <GfFields.Text {...field.node} key={id} />
48+
break
49+
50+
case 'phone':
51+
fieldToRender = <GfFields.Text {...field.node} key={id} />
52+
break
53+
3754
case 'text':
3855
fieldToRender = <GfFields.Text {...field.node} key={id} />
3956
break
4057

58+
case 'website':
59+
fieldToRender = <GfFields.Text {...field.node} key={id} />
60+
break
61+
4162
default:
4263
fieldToRender = (
4364
<pre key={id}>

components/molecules/GravityForm/Fields/Text.js

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,23 @@ import * as Input from '@/components/atoms/Inputs'
33
import {getGfFieldId, getGfHiddenClassName} from '@/functions/gravityForms'
44
import cn from 'classnames'
55

6+
/**
7+
* Render the GravityForm Text component.
8+
*
9+
* @author WebDevStudios
10+
* @param {object} props GravityForm field props.
11+
* @param {string} props.className Classname string.
12+
* @param {string} props.description GravityForm field description.
13+
* @param {boolean} props.enablePasswordInput GravityForm password enabled option.
14+
* @param {string} props.errorMessage GravityForm error message option.
15+
* @param {number} props.id GravityForm unique field id.
16+
* @param {boolean} props.isRequired GravityForm isRequired field.
17+
* @param {string} props.label GravityForm field label.
18+
* @param {string} props.size GravityForm field size.
19+
* @param {string} props.type GravityForm field type.
20+
* @param {boolean} props.visibility GravityForm visibility option.
21+
* @return {Element} The Text component.
22+
*/
623
export default function Text({
724
className,
825
description,
@@ -18,6 +35,32 @@ export default function Text({
1835
const fieldId = getGfFieldId(id)
1936
const isHiddenClass = getGfHiddenClassName(visibility)
2037

38+
/**
39+
* Convert type to an HTML input type.
40+
*
41+
* @param {string} type GravityForm field type.
42+
* @return {string} HTML input type.
43+
*/
44+
function modifyFieldType(type) {
45+
let inputType = type
46+
47+
if (enablePasswordInput && 'password') {
48+
inputType = 'password'
49+
}
50+
51+
if (type === 'phone') {
52+
inputType = 'tel'
53+
}
54+
55+
if (type === 'website') {
56+
inputType = 'url'
57+
}
58+
59+
return inputType
60+
}
61+
62+
const inputType = modifyFieldType(type)
63+
2164
return (
2265
<div
2366
className={cn(className, isHiddenClass) || null}
@@ -29,7 +72,7 @@ export default function Text({
2972
id={fieldId}
3073
isRequired={isRequired}
3174
label={label}
32-
type={(enablePasswordInput && 'password') || type}
75+
type={inputType}
3376
/>
3477
</div>
3578
)

components/molecules/GravityForm/GravityForm.js

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,20 @@ import Fields from './Fields'
44
import * as Yup from 'yup'
55
import getGfFieldId from '@/functions/gravityForms/getGfFieldId'
66
import {useState} from 'react'
7+
import styles from './GravityForm.module.css'
8+
import cn from 'classnames'
79

10+
/**
11+
* Render the GravityForm component.
12+
*
13+
* @param {object} props The GravityForm block attributes as props.
14+
* @param {object} props.formData GravityForm form data.
15+
* @param {string} props.formData.cssClass GravityForm form classname.
16+
* @param {object} props.formData.fields GravityForm form fields.
17+
* @param {number} props.formData.formId GravityForm form id.
18+
* @param {string} props.formData.title GravityForm form title.
19+
* @return {Element} The GravityForm component.
20+
*/
821
export default function GravityForm({
922
formData: {cssClass, fields, formId, title}
1023
}) {
@@ -15,8 +28,8 @@ export default function GravityForm({
1528
/**
1629
* Map field GravityForm ids and defaults to Object.
1730
*
18-
* @param {Array} fields Array of fields.
19-
* @return {Object} Default field values.
31+
* @param {Array} fields Array of fields.
32+
* @return {object} Default field values.
2033
*/
2134
function getFieldDefaults(fields) {
2235
const defaults = {}
@@ -43,7 +56,7 @@ export default function GravityForm({
4356

4457
return (
4558
<Form
46-
className={cssClass}
59+
className={cn(styles.gravityForm, cssClass)}
4760
formDefaults={fieldDefaults}
4861
id={formId && `gform-${formId}`}
4962
title={title}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.gravityForm {
2+
& > div {
3+
@apply mb-5;
4+
}
5+
}

0 commit comments

Comments
 (0)