Skip to content

Commit 9184e83

Browse files
author
Steven Than
committed
chore: wip
1 parent 0254c03 commit 9184e83

File tree

6 files changed

+198
-0
lines changed

6 files changed

+198
-0
lines changed

package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@
1616
".": {
1717
"require": "./lib/index.js",
1818
"default": "./esm/index.js"
19+
},
20+
"./hook-form": {
21+
"require": "./lib/components/HookForm/index.js",
22+
"default": "./esm/components/HookForm/index.js"
1923
}
2024
},
2125
"sideEffects": false,
@@ -73,6 +77,7 @@
7377
"lodash.without": "^4.4.0",
7478
"memoize-one": "^5.1.1",
7579
"prop-types": "^15.7.2",
80+
"react-hook-form": "^7.39.0",
7681
"react-imask": "^6.2.2",
7782
"react-resize-detector": "^4.2.3",
7883
"react-select-plus": "1.2.0",
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import React from 'react';
2+
import { SubmitHandler } from 'react-hook-form';
3+
import { FormFeedback } from 'reactstrap';
4+
import Button from '../Button/Button';
5+
import FormGroup from '../Form/FormGroup';
6+
import Label from '../Label/Label';
7+
import Form from './Form';
8+
import Input from './Input';
9+
10+
export default {
11+
title: 'react-hook-form',
12+
};
13+
14+
interface FormInputs {
15+
email: string;
16+
age: string;
17+
select: string;
18+
}
19+
20+
export const LiveExample = () => {
21+
const handleSubmit: SubmitHandler<FormInputs> = (formData) => {
22+
console.log(formData);
23+
};
24+
25+
return (
26+
<Form onSubmit={handleSubmit} mode="onChange">
27+
{({ formState: { errors, dirtyFields } }) => (
28+
<>
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>
80+
</>
81+
)}
82+
</Form>
83+
);
84+
};

src/components/HookForm/Form.tsx

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import React, { ReactNode } from 'react';
2+
import {
3+
useForm,
4+
FormProvider,
5+
SubmitHandler,
6+
UseFormProps,
7+
UseFormReturn,
8+
FieldValues,
9+
} from 'react-hook-form';
10+
import GearsForm from '../Form/Form';
11+
12+
interface FormProps<TFieldValues extends FieldValues> extends UseFormProps<TFieldValues> {
13+
onSubmit: SubmitHandler<TFieldValues>;
14+
children: (useFormReturn: UseFormReturn<TFieldValues>) => ReactNode | ReactNode;
15+
}
16+
17+
const Form = <TFieldValues extends FieldValues = FieldValues>({ children, onSubmit, ...useFormProps }: FormProps<TFieldValues>) => {
18+
const useFormReturn = useForm<TFieldValues>(useFormProps);
19+
20+
return (
21+
<FormProvider {...useFormReturn}>
22+
<GearsForm noValidate onSubmit={useFormReturn.handleSubmit(onSubmit)}>
23+
{typeof children === 'function' ? children(useFormReturn) : children}
24+
</GearsForm>
25+
</FormProvider>
26+
);
27+
};
28+
29+
export default Form;

src/components/HookForm/Input.tsx

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import React, { ComponentProps } from 'react';
2+
import { useFormContext, RegisterOptions, Validate, ValidationRule } from 'react-hook-form';
3+
import GearsInput from '../Input/Input';
4+
5+
type ValueAsNumber = boolean | undefined;
6+
type ValueAsDate = boolean | undefined;
7+
8+
type DetermineValidateValue<
9+
TValueAsNumber extends ValueAsNumber,
10+
TValueAsDate extends ValueAsDate
11+
> = TValueAsNumber extends true ? number : TValueAsDate extends true ? Date : string;
12+
13+
type InputProps<TValueAsNumber extends ValueAsNumber, TValueAsDate extends ValueAsDate> = Omit<
14+
ComponentProps<typeof GearsInput>,
15+
keyof RegisterOptions
16+
> &
17+
Omit<RegisterOptions, 'validate' | 'valueAsNumber' | 'valueAsDate'> & {
18+
name: string;
19+
validate?:
20+
| Validate<DetermineValidateValue<TValueAsNumber, TValueAsDate>>
21+
| Record<string, Validate<DetermineValidateValue<TValueAsNumber, TValueAsDate>>>;
22+
valueAsNumber?: TValueAsNumber;
23+
valueAsDate?: TValueAsDate;
24+
};
25+
26+
const extractValue = <TValidationValue extends boolean | number | string>(
27+
objOrValue?: ValidationRule<TValidationValue>
28+
) => (typeof objOrValue === 'object' ? objOrValue.value : objOrValue);
29+
30+
const Input = <
31+
TValueAsNumber extends ValueAsNumber = undefined,
32+
TValueAsDate extends ValueAsDate = undefined
33+
>({
34+
name,
35+
valueAsNumber,
36+
valueAsDate,
37+
validate,
38+
max,
39+
maxLength,
40+
min,
41+
minLength,
42+
pattern,
43+
required,
44+
...restProps
45+
}: InputProps<TValueAsNumber, TValueAsDate>) => {
46+
const { register } = useFormContext();
47+
const { ref, ...restRegister } = register(name, {
48+
valueAsNumber,
49+
valueAsDate,
50+
validate,
51+
max,
52+
min,
53+
pattern,
54+
required,
55+
});
56+
const gearsInputProps = {
57+
...restProps,
58+
max: extractValue(max),
59+
maxLength: extractValue(maxLength),
60+
min: extractValue(min),
61+
minLength: extractValue(minLength),
62+
required: !!required,
63+
};
64+
65+
return <GearsInput {...restRegister} innerRef={ref} {...gearsInputProps} />;
66+
};
67+
68+
export default Input;

src/components/HookForm/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export { default as Form } from './Form';
2+
export { default as Input } from './Input';

yarn.lock

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ __metadata:
140140
raf-stub: ^3.0.0
141141
react: ^16.14.0
142142
react-dom: ^16.14.0
143+
react-hook-form: ^7.39.0
143144
react-imask: ^6.2.2
144145
react-resize-detector: ^4.2.3
145146
react-select-plus: 1.2.0
@@ -14718,6 +14719,15 @@ __metadata:
1471814719
languageName: node
1471914720
linkType: hard
1472014721

14722+
"react-hook-form@npm:^7.39.0":
14723+
version: 7.39.0
14724+
resolution: "react-hook-form@npm:7.39.0"
14725+
peerDependencies:
14726+
react: ^16.8.0 || ^17 || ^18
14727+
checksum: f0f9a081ee634d1125ddde6e8896717d1416da95e1c07c57814a6421b9ab4cc8c057e8431c879c921dffde3d5cb1531cd8dfaa203f0a87e8a0da371a4f381a26
14728+
languageName: node
14729+
linkType: hard
14730+
1472114731
"react-imask@npm:^6.2.2":
1472214732
version: 6.4.2
1472314733
resolution: "react-imask@npm:6.4.2"

0 commit comments

Comments
 (0)