Skip to content

Commit ce95b0c

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

File tree

11 files changed

+383
-81
lines changed

11 files changed

+383
-81
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: 138 additions & 64 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,158 @@ 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> = (formData, { setError }) => {
30+
// make api calss
31+
// if fails then call setError
32+
setError('address.line1', {
33+
message: 'something went wrong with line1',
34+
});
2235
console.log(formData);
2336
};
2437

2538
return (
2639
<Form onSubmit={handleSubmit} mode="onChange">
27-
{({ formState: { errors, dirtyFields } }) => (
40+
{({ reset, formState: { errors, dirtyFields } }) => {
41+
console.log('render');
42+
console.log(errors);
43+
return (
44+
<>
45+
<div className="mb-3">
46+
<Label for="email">Email</Label>
47+
<FormFeedback>
48+
<Input id="email" name="email" required="Can't be blank" />
49+
</FormFeedback>
50+
51+
<FormFeedback name="email" />
52+
</div>
53+
<div className="mb-3">
54+
<legend>Address</legend>
55+
<Label for="line1">Address Line 1</Label>
56+
<Input invalid={!!errors.address?.line1} id="line1" name="address.line1" />
57+
<FormFeedback name="email" />
58+
<Label for="line2">Address Line 2</Label>
59+
<Input id="line2" name="address.addr2" />
60+
<Label for="state">State</Label>
61+
<Input id="state" name="address.state" />
62+
<Label for="zipCode">Zip Code</Label>
63+
<Input id="zipCode" name="address.zipCode" />
64+
</div>
65+
<div className="mb-3">
66+
<Label for="age">Age</Label>
67+
<Input
68+
min={{ value: 1, message: 'Min is 1' }}
69+
type="number"
70+
invalid={!!errors.age}
71+
id="age"
72+
name="age"
73+
/>
74+
<FormFeedback invalid>{errors.age?.message}</FormFeedback>
75+
</div>
76+
<div className="mb-3">
77+
<Label for="select">Select</Label>
78+
<Input type="select" invalid={!!errors.select} id="select" name="select">
79+
<option>1</option>
80+
<option>2</option>
81+
<option>3</option>
82+
<option>4</option>
83+
<option>5</option>
84+
</Input>
85+
<FormFeedback invalid>{errors.select?.message}</FormFeedback>
86+
</div>
87+
<div className="mb-3">
88+
<FormLabelGroup inputId="select-multiple" label="Select multiple" stacked>
89+
<Input type="select" id="select-multiple" name="selectMuliple" multiple>
90+
<option>1</option>
91+
<option>2</option>
92+
<option>3</option>
93+
<option>4</option>
94+
<option>5</option>
95+
</Input>
96+
</FormLabelGroup>
97+
</div>
98+
<div className="mb-3">
99+
<FormLabelGroup inputId="checkboxes" label="Check boxes" stacked>
100+
<Input type="checkbox" id="checkbox1" name="checkboxes" value="Value 1" />
101+
<Input type="checkbox" id="checkbox2" name="checkboxes" value="Value 2" />
102+
</FormLabelGroup>
103+
</div>
104+
<div className="mb-3">
105+
<legend>Radio Buttons</legend>
106+
<FormGroup check>
107+
<Input id="radio-option-1" name="radio" type="radio" value="radio-option-value-1" />{' '}
108+
<Label check for="radio-option-1">
109+
Option one is this and that—be sure to include why it‘s great
110+
</Label>
111+
</FormGroup>
112+
<FormGroup check>
113+
<Input id="radio-option-2" name="radio" type="radio" value="radio-option-value-2" />{' '}
114+
<Label check for="radio-option-2">
115+
Option two can be something else and selecting it will deselect option one
116+
</Label>
117+
</FormGroup>
118+
</div>
119+
<Button color="primary" type="submit">
120+
Submit
121+
</Button>
122+
<Button type="button" onClick={() => reset()}>
123+
Reset
124+
</Button>
125+
</>
126+
);
127+
}}
128+
</Form>
129+
);
130+
};
131+
132+
interface FormValues {
133+
email: string;
134+
}
135+
136+
export const SimpleFormNoValidation = () => {
137+
const handleSubmit: SubmitHandler<FormValues> = (formData) => {
138+
console.log(formData);
139+
};
140+
141+
return (
142+
<Form onSubmit={handleSubmit}>
143+
{({ formState: { isValid } }) => (
28144
<>
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>
145+
<FormRow name="test" type="month" />
146+
<Button type="submit" disabled={!isValid}>
147+
Submit
148+
</Button>
80149
</>
81150
)}
82151
</Form>
83152
);
84153
};
85154

86-
export const TestExample = () => {
87-
const handleSubmit: SubmitHandler<FormInputs> = (formData) => {
155+
export const FormDemo = () => {
156+
const handleSubmit: SubmitHandler<FormValues> = (formData, { reset }) => {
88157
console.log(formData);
158+
reset();
89159
};
90160

91161
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>
162+
<Form onSubmit={handleSubmit}>
163+
<FormLabelGroup label="First Name">
164+
<Input id="first-name" name="firstName" required="Can't be blank" />
165+
</FormLabelGroup>
166+
<FormLabelGroup label="Last Name">
167+
<Input id="last-name" name="lastName" />
168+
</FormLabelGroup>
169+
<Button color="primary" type="submit">
170+
Submit
171+
</Button>
98172
</Form>
99173
);
100174
};

src/components/HookForm/Form.tsx

Lines changed: 48 additions & 9 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+
type BaseFormProps<TFieldValues extends FieldValues> = {
1314
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)