Skip to content

Commit 4d5028e

Browse files
committed
feat: add partial support for union types (#16) (#26)
1 parent e494810 commit 4d5028e

File tree

12 files changed

+956
-24
lines changed

12 files changed

+956
-24
lines changed

examples/components/Checkbox.tsx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { type RegisterOptions } from 'react-hook-form';
2+
import { type HookFormControlShim, type Lens } from '@hookform/lenses';
3+
4+
export interface CheckboxProps
5+
extends Omit<
6+
RegisterOptions<HookFormControlShim<boolean>>,
7+
'valueAsNumber' | 'valueAsDate' | 'setValueAs' | 'onBlur' | 'onChange' | 'value'
8+
> {
9+
label: string;
10+
lens: Lens<boolean>;
11+
}
12+
13+
export function Checkbox({ label, lens, ...rules }: CheckboxProps) {
14+
const { control, name } = lens.interop();
15+
16+
return (
17+
<label>
18+
{label}
19+
<input type="checkbox" {...control.register(name, rules)} />
20+
</label>
21+
);
22+
}

examples/components/Select.tsx

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { type RegisterOptions } from 'react-hook-form';
2+
import { type HookFormControlShim, type Lens } from '@hookform/lenses';
3+
4+
export interface SelectOption<T extends string | number> {
5+
value: T;
6+
label: string;
7+
}
8+
9+
export interface SelectProps<T extends string | number>
10+
extends Omit<RegisterOptions<HookFormControlShim<T>>, 'valueAsNumber' | 'valueAsDate' | 'setValueAs' | 'onBlur' | 'onChange' | 'value'> {
11+
label: string;
12+
lens: Lens<T>;
13+
options: SelectOption<T>[];
14+
}
15+
16+
export function Select<T extends string | number>({ label, lens, options, ...rules }: SelectProps<T>) {
17+
const { control, name } = lens.interop();
18+
19+
return (
20+
<label>
21+
{label}&nbsp;
22+
{/* @ts-expect-error - TODO: fix this */}
23+
<select {...control.register(name, rules)}>
24+
{options.map((option) => (
25+
<option key={option.value} value={option.value}>
26+
{option.label}
27+
</option>
28+
))}
29+
</select>
30+
</label>
31+
);
32+
}

examples/typing/Optional.story.tsx

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { type SubmitHandler, useForm, useWatch } from 'react-hook-form';
2+
import { type Lens, useLens } from '@hookform/lenses';
3+
import { action } from '@storybook/addon-actions';
4+
import type { Meta, StoryObj } from '@storybook/react';
5+
import { fn } from '@storybook/test';
6+
7+
import { StringInput } from '../components/StringInput';
8+
9+
const meta = {
10+
title: 'Typing',
11+
component: Playground,
12+
} satisfies Meta;
13+
14+
export default meta;
15+
16+
type Story = StoryObj<typeof meta>;
17+
18+
interface FormValues {
19+
name?: string;
20+
surname?: string;
21+
}
22+
23+
interface PlaygroundProps {
24+
onSubmit: SubmitHandler<FormValues>;
25+
}
26+
27+
function Playground({ onSubmit = action('submit') }: PlaygroundProps) {
28+
const { handleSubmit, control } = useForm<FormValues>();
29+
const lens = useLens({ control });
30+
31+
return (
32+
<div>
33+
<form onSubmit={handleSubmit(onSubmit)}>
34+
<UserProfile
35+
lens={lens.narrow<{
36+
name: string;
37+
surname: string;
38+
}>()}
39+
/>
40+
<input type="submit" />
41+
</form>
42+
</div>
43+
);
44+
}
45+
46+
export const Optional: Story = {
47+
args: {
48+
onSubmit: fn(),
49+
},
50+
};
51+
52+
function UserProfile({
53+
lens,
54+
}: {
55+
lens: Lens<{
56+
name: string;
57+
surname: string;
58+
}>;
59+
}) {
60+
return (
61+
<div>
62+
<UserName lens={lens.focus('name')} />
63+
<StringInput label="Surname" lens={lens.focus('surname')} />
64+
</div>
65+
);
66+
}
67+
68+
function UserName({ lens }: { lens: Lens<string> }) {
69+
const value = useWatch(lens.interop());
70+
71+
if (value === undefined || value === null) {
72+
return null;
73+
}
74+
75+
return <StringInput label="Name" lens={lens} />;
76+
}

0 commit comments

Comments
 (0)