Skip to content

Commit

Permalink
feat: add field checkbox and fieldswitch (#509)
Browse files Browse the repository at this point in the history
* feat: add field checkbox and fieldswitch

update their theme

* fix: update FieldCheckbox and FieldSwitch API

---------

Co-authored-by: Ivan Dalmet <[email protected]>
  • Loading branch information
yoannfleurydev and ivan-dalmet authored Jul 22, 2024
1 parent 2ce1610 commit f098bd5
Show file tree
Hide file tree
Showing 10 changed files with 692 additions and 2 deletions.
129 changes: 129 additions & 0 deletions src/components/Form/FieldCheckbox/FieldCheckbox.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import { FormLabel } from '@chakra-ui/react';
import { expect, test, vi } from 'vitest';
import { z } from 'zod';

import { render, screen, setupUser } from '@/tests/utils';

import { FormField } from '../FormField';
import { FormFieldController } from '../FormFieldController';
import { FormMocked } from '../form-test-utils';

test('update value', async () => {
const user = setupUser();
const mockedSubmit = vi.fn();

render(
<FormMocked
schema={z.object({
doit: z.boolean().default(false),
})}
onSubmit={mockedSubmit}
>
{({ form }) => (
<FormField>
<FormLabel>Should I do something?</FormLabel>
<FormFieldController
type="checkbox"
control={form.control}
name="doit"
label="Yes, do it!"
/>
</FormField>
)}
</FormMocked>
);
await user.click(screen.getByLabelText('Should I do something?'));
await user.click(screen.getByRole('button', { name: 'Submit' }));
expect(mockedSubmit).toHaveBeenCalledWith({ doit: true });
});

test('double click', async () => {
const user = setupUser();
const mockedSubmit = vi.fn();

render(
<FormMocked
schema={z.object({
doit: z.boolean().default(false),
})}
onSubmit={mockedSubmit}
>
{({ form }) => (
<FormField>
<FormLabel>Should I do something?</FormLabel>
<FormFieldController
type="checkbox"
control={form.control}
name="doit"
label="Yes, do it!"
/>
</FormField>
)}
</FormMocked>
);
await user.click(screen.getByLabelText('Should I do something?'));
await user.click(screen.getByLabelText('Should I do something?'));
await user.click(screen.getByRole('button', { name: 'Submit' }));
expect(mockedSubmit).toHaveBeenCalledWith({ doit: false });
});

test('default value', async () => {
const user = setupUser();
const mockedSubmit = vi.fn();

render(
<FormMocked
schema={z.object({
doit: z.boolean().default(false),
})}
useFormOptions={{ defaultValues: { doit: true } }}
onSubmit={mockedSubmit}
>
{({ form }) => (
<FormField>
<FormLabel>Should I do something?</FormLabel>
<FormFieldController
type="checkbox"
control={form.control}
name="doit"
label="Yes, do it!"
/>
</FormField>
)}
</FormMocked>
);
await user.click(screen.getByLabelText('Should I do something?'));
await user.click(screen.getByRole('button', { name: 'Submit' }));
expect(mockedSubmit).toHaveBeenCalledWith({ doit: false });
});

test('disabled', async () => {
const user = setupUser();
const mockedSubmit = vi.fn();

render(
<FormMocked
schema={z.object({
doit: z.boolean().default(false),
})}
useFormOptions={{ defaultValues: { doit: false } }}
onSubmit={mockedSubmit}
>
{({ form }) => (
<FormField>
<FormLabel>Should I do something?</FormLabel>
<FormFieldController
type="checkbox"
control={form.control}
name="doit"
isDisabled
label="Yes, do it!"
/>
</FormField>
)}
</FormMocked>
);
await user.click(screen.getByLabelText('Should I do something?'));
await user.click(screen.getByRole('button', { name: 'Submit' }));
expect(mockedSubmit).toHaveBeenCalledWith({ doit: false });
});
143 changes: 143 additions & 0 deletions src/components/Form/FieldCheckbox/docs.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import { Box, Button, Stack } from '@chakra-ui/react';
import { zodResolver } from '@hookform/resolvers/zod';
import { useForm } from 'react-hook-form';
import { z } from 'zod';

import {
Form,
FormField,
FormFieldController,
FormFieldHelper,
FormFieldLabel,
} from '../';

export default {
title: 'Form/FieldCheckbox',
};

type FormSchema = z.infer<ReturnType<typeof zFormSchema>>;
const zFormSchema = () =>
z.object({
doit: z.literal(true),
});

const formOptions = {
mode: 'onBlur',
resolver: zodResolver(zFormSchema()),
} as const;

export const Default = () => {
const form = useForm<FormSchema>(formOptions);

return (
<Form {...form} onSubmit={(values) => console.log(values)}>
<Stack spacing={4}>
<FormField>
<FormFieldLabel>Should I do something?</FormFieldLabel>
<FormFieldController
control={form.control}
type="checkbox"
name="doit"
label="Yes, do it!"
/>
<FormFieldHelper>Helper</FormFieldHelper>
</FormField>
<Box>
<Button type="submit" variant="@primary">
Submit
</Button>
</Box>
</Stack>
</Form>
);
};

export const DefaultValues = () => {
const form = useForm<FormSchema>({
...formOptions,
defaultValues: {
doit: true,
},
});

return (
<Form {...form} onSubmit={(values) => console.log(values)}>
<Stack spacing={4}>
<FormField>
<FormFieldLabel>Should I do something?</FormFieldLabel>
<FormFieldController
control={form.control}
type="checkbox"
name="doit"
label="Yes, do it!"
/>
<FormFieldHelper>Helper</FormFieldHelper>
</FormField>
<Box>
<Button type="submit" variant="@primary">
Submit
</Button>
</Box>
</Stack>
</Form>
);
};

export const Disabled = () => {
const form = useForm<FormSchema>(formOptions);

return (
<Form {...form} onSubmit={(values) => console.log(values)}>
<Stack spacing={4}>
<FormField>
<FormFieldLabel>Should I do something?</FormFieldLabel>
<FormFieldController
control={form.control}
type="checkbox"
name="doit"
isDisabled
label="Yes, do it!"
/>
<FormFieldHelper>Helper</FormFieldHelper>
</FormField>
<Box>
<Button type="submit" variant="@primary">
Submit
</Button>
</Box>
</Stack>
</Form>
);
};

export const DisabledDefaultValues = () => {
const form = useForm<FormSchema>({
...formOptions,
defaultValues: {
doit: true,
},
});

return (
<Form {...form} onSubmit={(values) => console.log(values)}>
<Stack spacing={4}>
<FormField>
<FormFieldLabel>Should I do something?</FormFieldLabel>
<FormFieldController
control={form.control}
type="checkbox"
name="doit"
isDisabled
label="Yes, do it!"
/>
<FormFieldHelper>Helper</FormFieldHelper>
</FormField>
<Box>
<Button type="submit" variant="@primary">
Submit
</Button>
</Box>
</Stack>
</Form>
);
};
58 changes: 58 additions & 0 deletions src/components/Form/FieldCheckbox/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { ReactNode } from 'react';

import { Checkbox, CheckboxProps, Flex, FlexProps } from '@chakra-ui/react';
import {
Controller,
ControllerRenderProps,
FieldPath,
FieldValues,
} from 'react-hook-form';

import { FieldCommonProps } from '../FormFieldController';
import { FormFieldError } from '../FormFieldError';

export type CheckboxRootProps = Pick<CheckboxProps, 'size'>;

export type FieldCheckboxProps<
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
> = {
type: 'checkbox';
label?: ReactNode;
checkboxProps?: RemoveFromType<
RemoveFromType<
Omit<CheckboxProps, 'isChecked' | 'isDisabled' | 'children'>,
CheckboxRootProps
>,
ControllerRenderProps
>;
containerProps?: FlexProps;
} & CheckboxRootProps &
FieldCommonProps<TFieldValues, TName>;

export const FieldCheckbox = <
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
>(
props: FieldCheckboxProps<TFieldValues, TName>
) => {
return (
<Controller
{...props}
render={({ field: { value, ...field } }) => (
<Flex flexDirection="column" gap={1} flex={1} {...props.containerProps}>
<Checkbox
size={props.size}
isChecked={!!value}
isDisabled={props.isDisabled}
{...props.checkboxProps}
{...field}
>
{props.label}
</Checkbox>
<FormFieldError />
</Flex>
)}
/>
);
};
Loading

0 comments on commit f098bd5

Please sign in to comment.