Skip to content

Commit

Permalink
chore: wip
Browse files Browse the repository at this point in the history
  • Loading branch information
Steven Than committed Nov 4, 2022
1 parent 0254c03 commit 9184e83
Show file tree
Hide file tree
Showing 6 changed files with 198 additions and 0 deletions.
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@
".": {
"require": "./lib/index.js",
"default": "./esm/index.js"
},
"./hook-form": {
"require": "./lib/components/HookForm/index.js",
"default": "./esm/components/HookForm/index.js"
}
},
"sideEffects": false,
Expand Down Expand Up @@ -73,6 +77,7 @@
"lodash.without": "^4.4.0",
"memoize-one": "^5.1.1",
"prop-types": "^15.7.2",
"react-hook-form": "^7.39.0",
"react-imask": "^6.2.2",
"react-resize-detector": "^4.2.3",
"react-select-plus": "1.2.0",
Expand Down
84 changes: 84 additions & 0 deletions src/components/HookForm/Form.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import React from 'react';
import { SubmitHandler } from 'react-hook-form';
import { FormFeedback } from 'reactstrap';
import Button from '../Button/Button';
import FormGroup from '../Form/FormGroup';
import Label from '../Label/Label';
import Form from './Form';
import Input from './Input';

export default {
title: 'react-hook-form',
};

interface FormInputs {
email: string;
age: string;
select: string;
}

export const LiveExample = () => {
const handleSubmit: SubmitHandler<FormInputs> = (formData) => {
console.log(formData);
};

return (
<Form onSubmit={handleSubmit} mode="onChange">
{({ formState: { errors, dirtyFields } }) => (
<>
<div className="mb-3">
<Label for="email">Email</Label>
<Input
valid={dirtyFields.email && !errors.email}
invalid={!!errors.email}
id="email"
name="email"
validate={(value) => value === 'email' || 'incorrect'}
/>
<FormFeedback invalid>{errors.email?.message}</FormFeedback>
<FormFeedback valid>Looks good!</FormFeedback>
</div>
<div className="mb-3">
<Label for="age">Age</Label>
<Input
min={{ value: 1, message: 'Min is 1' }}
type="number"
invalid={!!errors.age}
id="age"
name="age"
required="Can't be blank"
/>
<FormFeedback invalid>{errors.age?.message}</FormFeedback>
</div>
<div className="mb-3">
<Label for="select">Select</Label>
<Input type="select" invalid={!!errors.select} id="select" name="select">
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
<option>5</option>
</Input>
<FormFeedback invalid>{errors.select?.message}</FormFeedback>
</div>
<div className="mb-3">
<legend>Radio Buttons</legend>
<FormGroup check>
<Input id="radio-option-1" name="radio" type="radio" value="radio-option-value-1" />{' '}
<Label check for="radio-option-1">
Option one is this and that—be sure to include why it‘s great
</Label>
</FormGroup>
<FormGroup check>
<Input id="radio-option-2" name="radio" type="radio" value="radio-option-value-2" />{' '}
<Label check for="radio-option-2">
Option two can be something else and selecting it will deselect option one
</Label>
</FormGroup>
</div>
<Button type="submit">Submit</Button>
</>
)}
</Form>
);
};
29 changes: 29 additions & 0 deletions src/components/HookForm/Form.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React, { ReactNode } from 'react';
import {
useForm,
FormProvider,
SubmitHandler,
UseFormProps,
UseFormReturn,
FieldValues,
} from 'react-hook-form';
import GearsForm from '../Form/Form';

interface FormProps<TFieldValues extends FieldValues> extends UseFormProps<TFieldValues> {
onSubmit: SubmitHandler<TFieldValues>;
children: (useFormReturn: UseFormReturn<TFieldValues>) => ReactNode | ReactNode;
}

const Form = <TFieldValues extends FieldValues = FieldValues>({ children, onSubmit, ...useFormProps }: FormProps<TFieldValues>) => {
const useFormReturn = useForm<TFieldValues>(useFormProps);

return (
<FormProvider {...useFormReturn}>
<GearsForm noValidate onSubmit={useFormReturn.handleSubmit(onSubmit)}>
{typeof children === 'function' ? children(useFormReturn) : children}
</GearsForm>
</FormProvider>
);
};

export default Form;
68 changes: 68 additions & 0 deletions src/components/HookForm/Input.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import React, { ComponentProps } from 'react';
import { useFormContext, RegisterOptions, Validate, ValidationRule } from 'react-hook-form';
import GearsInput from '../Input/Input';

type ValueAsNumber = boolean | undefined;
type ValueAsDate = boolean | undefined;

type DetermineValidateValue<
TValueAsNumber extends ValueAsNumber,
TValueAsDate extends ValueAsDate
> = TValueAsNumber extends true ? number : TValueAsDate extends true ? Date : string;

type InputProps<TValueAsNumber extends ValueAsNumber, TValueAsDate extends ValueAsDate> = Omit<
ComponentProps<typeof GearsInput>,
keyof RegisterOptions
> &
Omit<RegisterOptions, 'validate' | 'valueAsNumber' | 'valueAsDate'> & {
name: string;
validate?:
| Validate<DetermineValidateValue<TValueAsNumber, TValueAsDate>>
| Record<string, Validate<DetermineValidateValue<TValueAsNumber, TValueAsDate>>>;
valueAsNumber?: TValueAsNumber;
valueAsDate?: TValueAsDate;
};

const extractValue = <TValidationValue extends boolean | number | string>(
objOrValue?: ValidationRule<TValidationValue>
) => (typeof objOrValue === 'object' ? objOrValue.value : objOrValue);

const Input = <
TValueAsNumber extends ValueAsNumber = undefined,
TValueAsDate extends ValueAsDate = undefined
>({
name,
valueAsNumber,
valueAsDate,
validate,
max,
maxLength,
min,
minLength,
pattern,
required,
...restProps
}: InputProps<TValueAsNumber, TValueAsDate>) => {
const { register } = useFormContext();
const { ref, ...restRegister } = register(name, {
valueAsNumber,
valueAsDate,
validate,
max,
min,
pattern,
required,
});
const gearsInputProps = {
...restProps,
max: extractValue(max),
maxLength: extractValue(maxLength),
min: extractValue(min),
minLength: extractValue(minLength),
required: !!required,
};

return <GearsInput {...restRegister} innerRef={ref} {...gearsInputProps} />;
};

export default Input;
2 changes: 2 additions & 0 deletions src/components/HookForm/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { default as Form } from './Form';
export { default as Input } from './Input';
10 changes: 10 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ __metadata:
raf-stub: ^3.0.0
react: ^16.14.0
react-dom: ^16.14.0
react-hook-form: ^7.39.0
react-imask: ^6.2.2
react-resize-detector: ^4.2.3
react-select-plus: 1.2.0
Expand Down Expand Up @@ -14718,6 +14719,15 @@ __metadata:
languageName: node
linkType: hard

"react-hook-form@npm:^7.39.0":
version: 7.39.0
resolution: "react-hook-form@npm:7.39.0"
peerDependencies:
react: ^16.8.0 || ^17 || ^18
checksum: f0f9a081ee634d1125ddde6e8896717d1416da95e1c07c57814a6421b9ab4cc8c057e8431c879c921dffde3d5cb1531cd8dfaa203f0a87e8a0da371a4f381a26
languageName: node
linkType: hard

"react-imask@npm:^6.2.2":
version: 6.4.2
resolution: "react-imask@npm:6.4.2"
Expand Down

0 comments on commit 9184e83

Please # to comment.