Skip to content

Commit 69a6fb9

Browse files
author
Steven Than
committed
chore: wip
1 parent 1ec016f commit 69a6fb9

File tree

11 files changed

+442
-83
lines changed

11 files changed

+442
-83
lines changed

.eslintrc.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"extends": ["@appfolio/eslint-config-appfolio-react", "prettier"],
44
"plugins": ["no-only-tests", "react-hooks", "@typescript-eslint"],
55
"rules": {
6+
"camelcase": ["error", { "allow": "^experimental_" }],
67
"curly": ["error", "all"],
78
"react/jsx-props-no-spreading": "off",
89
"react/static-property-placement": "off",

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@appfolio/react-gears",
3-
"version": "7.9.0",
3+
"version": "7.10.0-hook-form.8",
44
"description": "React-based version of Gears",
55
"author": "Appfolio, Inc.",
66
"repository": {

src/components/Form/FormRow.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ const gearsInputs = {
1717

1818
type PropOrDefault<T extends {}, K extends PropertyKey, D = never> = K extends keyof T ? T[K] : D;
1919
type ReactStrapInputTypes = NonNullable<React.ComponentProps<typeof Input>['type']>;
20-
type InputTypes = ReactStrapInputTypes | keyof typeof gearsInputs;
20+
export type InputTypes = ReactStrapInputTypes | keyof typeof gearsInputs;
2121

2222
function getInputByType<T extends InputTypes>(type: T) {
2323
return (
Lines changed: 196 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import React from 'react';
2-
import { SubmitHandler } from 'react-hook-form';
3-
import { FormFeedback } from 'reactstrap';
42
import Button from '../Button/Button';
53
import FormGroup from '../Form/FormGroup';
64
import Label from '../Label/Label';
75
import Form from './Form';
6+
import FormFeedback from './FormFeedback';
7+
import FormLabelGroup from './FormLabelGroup';
8+
import FormRow from './FormRow';
89
import Input from './Input';
10+
import { SubmitHandler } from './types';
911

1012
export default {
1113
title: 'react-hook-form',
@@ -15,86 +17,215 @@ interface FormInputs {
1517
email: string;
1618
age: string;
1719
select: string;
20+
address: {
21+
line1: string;
22+
line2: string;
23+
state: string;
24+
zipCode: string;
25+
};
1826
}
1927

20-
export const LiveExample = () => {
21-
const handleSubmit: SubmitHandler<FormInputs> = (formData) => {
28+
export const FormWithValidations = () => {
29+
const handleSubmit: SubmitHandler<FormInputs> = (
30+
formData,
31+
{ setError }
32+
) => {
33+
// make api calss
34+
// if fails then call setError
35+
setError('address.line1', {
36+
message: 'something went wrong with line1',
37+
});
2238
console.log(formData);
2339
};
2440

2541
return (
2642
<Form onSubmit={handleSubmit} mode="onChange">
27-
{({ formState: { errors, dirtyFields } }) => (
43+
{({ reset, formState: { errors, dirtyFields } }) => {
44+
console.log('render');
45+
console.log(errors);
46+
return (
47+
<>
48+
<div className="mb-3">
49+
50+
<Label for="email">Email</Label>
51+
<FormFeedback>
52+
<Input id="email" name="email" required="Can't be blank" />
53+
</FormFeedback>
54+
55+
<FormFeedback name="email" />
56+
57+
</div>
58+
<div className="mb-3">
59+
<legend>Address</legend>
60+
<Label for="line1">Address Line 1</Label>
61+
<Input
62+
invalid={!!errors.address?.line1}
63+
id="line1"
64+
name="address.line1"
65+
/>
66+
<FormFeedback name="email" />
67+
<Label for="line2">Address Line 2</Label>
68+
<Input id="line2" name="address.addr2" />
69+
<Label for="state">State</Label>
70+
<Input id="state" name="address.state" />
71+
<Label for="zipCode">Zip Code</Label>
72+
<Input id="zipCode" name="address.zipCode" />
73+
</div>
74+
<div className="mb-3">
75+
<Label for="age">Age</Label>
76+
<Input
77+
min={{ value: 1, message: 'Min is 1' }}
78+
type="number"
79+
invalid={!!errors.age}
80+
id="age"
81+
name="age"
82+
/>
83+
<FormFeedback invalid>
84+
{errors.age?.message}
85+
</FormFeedback>
86+
</div>
87+
<div className="mb-3">
88+
<Label for="select">Select</Label>
89+
<Input
90+
type="select"
91+
invalid={!!errors.select}
92+
id="select"
93+
name="select"
94+
>
95+
<option>1</option>
96+
<option>2</option>
97+
<option>3</option>
98+
<option>4</option>
99+
<option>5</option>
100+
</Input>
101+
<FormFeedback invalid>
102+
{errors.select?.message}
103+
</FormFeedback>
104+
</div>
105+
<div className="mb-3">
106+
<FormLabelGroup
107+
inputId="select-multiple"
108+
label="Select multiple"
109+
stacked
110+
>
111+
<Input
112+
type="select"
113+
id="select-multiple"
114+
name="selectMuliple"
115+
multiple
116+
>
117+
<option>1</option>
118+
<option>2</option>
119+
<option>3</option>
120+
<option>4</option>
121+
<option>5</option>
122+
</Input>
123+
</FormLabelGroup>
124+
</div>
125+
<div className="mb-3">
126+
<FormLabelGroup
127+
inputId="checkboxes"
128+
label="Check boxes"
129+
stacked
130+
>
131+
<Input
132+
type="checkbox"
133+
id="checkbox1"
134+
name="checkboxes"
135+
value="Value 1"
136+
/>
137+
<Input
138+
type="checkbox"
139+
id="checkbox2"
140+
name="checkboxes"
141+
value="Value 2"
142+
/>
143+
</FormLabelGroup>
144+
</div>
145+
<div className="mb-3">
146+
<legend>Radio Buttons</legend>
147+
<FormGroup check>
148+
<Input
149+
id="radio-option-1"
150+
name="radio"
151+
type="radio"
152+
value="radio-option-value-1"
153+
/>{' '}
154+
<Label check for="radio-option-1">
155+
Option one is this and that—be sure to include
156+
why it‘s great
157+
</Label>
158+
</FormGroup>
159+
<FormGroup check>
160+
<Input
161+
id="radio-option-2"
162+
name="radio"
163+
type="radio"
164+
value="radio-option-value-2"
165+
/>{' '}
166+
<Label check for="radio-option-2">
167+
Option two can be something else and selecting
168+
it will deselect option one
169+
</Label>
170+
</FormGroup>
171+
</div>
172+
<Button color="primary" type="submit">
173+
Submit
174+
</Button>
175+
<Button type="button" onClick={() => reset()}>
176+
Reset
177+
</Button>
178+
</>
179+
);
180+
}}
181+
</Form>
182+
);
183+
};
184+
185+
interface FormValues {
186+
email: string;
187+
}
188+
189+
export const SimpleFormNoValidation = () => {
190+
const handleSubmit: SubmitHandler<FormValues> = (formData) => {
191+
console.log(formData);
192+
};
193+
194+
return (
195+
<Form onSubmit={handleSubmit}>
196+
{({ formState: { isValid } }) => (
28197
<>
29-
<div className="mb-3">
30-
<Label for="email">Email</Label>
31-
<Input
32-
valid={dirtyFields.email && !errors.email}
33-
invalid={!!errors.email}
34-
id="email"
35-
name="email"
36-
validate={(value) => value === 'email' || 'incorrect'}
37-
/>
38-
<FormFeedback invalid>{errors.email?.message}</FormFeedback>
39-
<FormFeedback valid>Looks good!</FormFeedback>
40-
</div>
41-
<div className="mb-3">
42-
<Label for="age">Age</Label>
43-
<Input
44-
min={{ value: 1, message: 'Min is 1' }}
45-
type="number"
46-
invalid={!!errors.age}
47-
id="age"
48-
name="age"
49-
required="Can't be blank"
50-
/>
51-
<FormFeedback invalid>{errors.age?.message}</FormFeedback>
52-
</div>
53-
<div className="mb-3">
54-
<Label for="select">Select</Label>
55-
<Input type="select" invalid={!!errors.select} id="select" name="select">
56-
<option>1</option>
57-
<option>2</option>
58-
<option>3</option>
59-
<option>4</option>
60-
<option>5</option>
61-
</Input>
62-
<FormFeedback invalid>{errors.select?.message}</FormFeedback>
63-
</div>
64-
<div className="mb-3">
65-
<legend>Radio Buttons</legend>
66-
<FormGroup check>
67-
<Input id="radio-option-1" name="radio" type="radio" value="radio-option-value-1" />{' '}
68-
<Label check for="radio-option-1">
69-
Option one is this and that—be sure to include why it‘s great
70-
</Label>
71-
</FormGroup>
72-
<FormGroup check>
73-
<Input id="radio-option-2" name="radio" type="radio" value="radio-option-value-2" />{' '}
74-
<Label check for="radio-option-2">
75-
Option two can be something else and selecting it will deselect option one
76-
</Label>
77-
</FormGroup>
78-
</div>
79-
<Button type="submit">Submit</Button>
198+
<FormRow name="test" type="month" />
199+
<Button type="submit" disabled={!isValid}>
200+
Submit
201+
</Button>
80202
</>
81203
)}
82204
</Form>
83205
);
84206
};
85207

86-
export const TestExample = () => {
87-
const handleSubmit: SubmitHandler<FormInputs> = (formData) => {
208+
export const FormDemo = () => {
209+
const handleSubmit: SubmitHandler<FormValues> = (formData) => {
88210
console.log(formData);
89211
};
90212

91213
return (
92-
<Form onSubmit={handleSubmit} mode="onChange">
93-
<div className="mb-3">
94-
<Label for="email">Email</Label>
95-
<Input id="email" name="email" />
96-
<FormFeedback valid>Looks good!</FormFeedback>
97-
</div>
98-
</Form>
214+
215+
<Form onSubmit={handleSubmit}>
216+
<FormLabelGroup label="First Name">
217+
<Input
218+
id="first-name"
219+
name="firstName"
220+
required="Can't be blank"
221+
/>
222+
</FormLabelGroup>
223+
<FormLabelGroup label="Last Name">
224+
<Input id="last-name" name="lastName" />
225+
</FormLabelGroup>
226+
<FormLabelGroup label={<></>}>
227+
<Button color="primary" type="submit">Submit</Button>
228+
</FormLabelGroup>
229+
</Form>
99230
);
100231
};

src/components/HookForm/Form.tsx

Lines changed: 49 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,68 @@
1-
import React, { ReactNode } from 'react';
1+
import React, { ReactNode, ComponentProps } from 'react';
22
import {
33
useForm,
44
FormProvider,
5-
SubmitHandler,
5+
SubmitHandler as HookFormSubmitHandler,
66
UseFormProps,
77
UseFormReturn,
88
FieldValues,
99
} from 'react-hook-form';
1010
import GearsForm from '../Form/Form';
11+
import { SubmitHandler } from './types';
1112

12-
interface FormProps<TFieldValues extends FieldValues> extends UseFormProps<TFieldValues> {
13-
onSubmit: SubmitHandler<TFieldValues>;
13+
type BaseFormProps<TFieldValues extends FieldValues> = {
14+
onSubmit?: SubmitHandler<TFieldValues>;
1415
children: ((useFormReturn: UseFormReturn<TFieldValues>) => ReactNode) | ReactNode;
15-
}
16+
};
17+
18+
type FormProps<TFieldValues extends FieldValues> = Omit<
19+
ComponentProps<typeof GearsForm>,
20+
keyof BaseFormProps<TFieldValues>
21+
> &
22+
UseFormProps<TFieldValues> &
23+
BaseFormProps<TFieldValues>;
1624

17-
const Form = <TFieldValues extends FieldValues = FieldValues>({
25+
const Form = <TFieldValues extends FieldValues = FieldValues, TContext = any>({
1826
children,
19-
onSubmit,
20-
...useFormProps
27+
action,
28+
method,
29+
onSubmit = () => undefined,
30+
mode,
31+
reValidateMode,
32+
defaultValues,
33+
resolver,
34+
context,
35+
criteriaMode,
36+
shouldFocusError,
37+
shouldUnregister,
38+
shouldUseNativeValidation,
39+
delayError,
40+
...gearsFormProps
2141
}: FormProps<TFieldValues>) => {
22-
const useFormReturn = useForm<TFieldValues>(useFormProps);
42+
const useFormReturn = useForm<TFieldValues, TContext>({
43+
mode,
44+
reValidateMode,
45+
defaultValues,
46+
resolver,
47+
context,
48+
criteriaMode,
49+
shouldFocusError,
50+
shouldUnregister,
51+
shouldUseNativeValidation,
52+
delayError,
53+
});
54+
55+
const handleFormSubmit: HookFormSubmitHandler<TFieldValues> = async (formData, event) => {
56+
return await onSubmit(formData, { ...useFormReturn }, event);
57+
};
2358

2459
return (
2560
<FormProvider {...useFormReturn}>
26-
<GearsForm noValidate onSubmit={useFormReturn.handleSubmit(onSubmit)}>
61+
<GearsForm
62+
noValidate
63+
onSubmit={useFormReturn.handleSubmit(handleFormSubmit)}
64+
{...gearsFormProps}
65+
>
2766
{typeof children === 'function' ? children(useFormReturn) : children}
2867
</GearsForm>
2968
</FormProvider>

0 commit comments

Comments
 (0)