live lesson 13-01-24

This commit is contained in:
2024-01-15 09:53:43 +01:00
parent 06afed2826
commit 5854cab150
7 changed files with 181 additions and 50 deletions

View File

@@ -14,11 +14,14 @@
},
"dependencies": {
"@fontsource/roboto": "^5.0.5",
"@hookform/resolvers": "^3.3.4",
"@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-label": "^2.0.2",
"clsx": "^2.0.0",
"react": "^18.2.0",
"react-dom": "^18.2.0"
"react-dom": "^18.2.0",
"react-hook-form": "^7.49.3",
"zod": "^3.22.4"
},
"devDependencies": {
"@it-incubator/eslint-config": "^0.1.3",

30
pnpm-lock.yaml generated
View File

@@ -8,6 +8,9 @@ dependencies:
'@fontsource/roboto':
specifier: ^5.0.5
version: 5.0.5
'@hookform/resolvers':
specifier: ^3.3.4
version: 3.3.4(react-hook-form@7.49.3)
'@radix-ui/react-checkbox':
specifier: ^1.0.4
version: 1.0.4(@types/react-dom@18.2.7)(@types/react@18.2.15)(react-dom@18.2.0)(react@18.2.0)
@@ -23,6 +26,12 @@ dependencies:
react-dom:
specifier: ^18.2.0
version: 18.2.0(react@18.2.0)
react-hook-form:
specifier: ^7.49.3
version: 7.49.3(react@18.2.0)
zod:
specifier: ^3.22.4
version: 3.22.4
devDependencies:
'@it-incubator/eslint-config':
@@ -1747,6 +1756,14 @@ packages:
resolution: {integrity: sha512-IMXFq5AMgGx0sgNLfwWsmPuy3qa7lmDmQcXXihqwF4mT2UpD725cbxZj93ERY793OWon+6V1ANax02I3nt9+4w==}
dev: false
/@hookform/resolvers@3.3.4(react-hook-form@7.49.3):
resolution: {integrity: sha512-o5cgpGOuJYrd+iMKvkttOclgwRW86EsWJZZRC23prf0uU2i48Htq4PuT73AVb9ionFyZrwYEITuOFGF+BydEtQ==}
peerDependencies:
react-hook-form: ^7.0.0
dependencies:
react-hook-form: 7.49.3(react@18.2.0)
dev: false
/@humanwhocodes/config-array@0.11.10:
resolution: {integrity: sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==}
engines: {node: '>=10.10.0'}
@@ -7541,6 +7558,15 @@ packages:
react-is: 18.1.0
dev: true
/react-hook-form@7.49.3(react@18.2.0):
resolution: {integrity: sha512-foD6r3juidAT1cOZzpmD/gOKt7fRsDhXXZ0y28+Al1CHgX+AY1qIN9VSIIItXRq1dN68QrRwl1ORFlwjBaAqeQ==}
engines: {node: '>=18', pnpm: '8'}
peerDependencies:
react: ^16.8.0 || ^17 || ^18
dependencies:
react: 18.2.0
dev: false
/react-inspector@6.0.2(react@18.2.0):
resolution: {integrity: sha512-x+b7LxhmHXjHoU/VrFAzw5iutsILRoYyDq97EDYdFpPLcvqtEzk4ZSZSQjnFPbr5T57tLXnHcqFYoN1pI6u8uQ==}
peerDependencies:
@@ -9139,3 +9165,7 @@ packages:
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
engines: {node: '>=10'}
dev: true
/zod@3.22.4:
resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==}
dev: false

View File

@@ -0,0 +1,14 @@
import type { Meta, StoryObj } from '@storybook/react'
import { LoginForm } from './login-form'
const meta = {
title: 'Auth/LoginForm',
component: LoginForm,
tags: ['autodocs'],
} satisfies Meta<typeof LoginForm>
export default meta
type Story = StoryObj<typeof meta>
export const Primary: Story = {}

View File

@@ -0,0 +1,52 @@
import { zodResolver } from '@hookform/resolvers/zod'
import { useForm } from 'react-hook-form'
import { z } from 'zod'
import { Button } from '../../ui/button'
import { ControlledCheckbox } from '@/components/ui/controlled/controlled-checkbox.tsx'
import { ControlledTextField } from '@/components/ui/controlled/controlled-text-field.tsx'
const loginSchema = z.object({
email: z.string().email(''),
password: z.string().min(3).max(30),
rememberMe: z.literal(true),
})
type FormValues = z.infer<typeof loginSchema>
export const LoginForm = () => {
const {
handleSubmit,
control,
formState: { errors },
} = useForm<FormValues>({
resolver: zodResolver(loginSchema),
})
const onSubmit = (data: FormValues) => {
console.log(data)
}
return (
<form onSubmit={handleSubmit(onSubmit)}>
<ControlledTextField
control={control}
name={'email'}
label={'email'}
errorMessage={errors.email?.message}
/>
<ControlledTextField
control={control}
name={'password'}
label={'password'}
errorMessage={errors.password?.message}
/>
<ControlledCheckbox
control={control}
name={'rememberMe'}
label="I accept terms and conditions"
/>
<Button type="submit">Submit</Button>
</form>
)
}

View File

@@ -1,4 +1,4 @@
import { FC } from 'react'
import { ComponentPropsWithoutRef, forwardRef, ElementRef } from 'react'
import * as CheckboxRadix from '@radix-ui/react-checkbox'
import * as LabelRadix from '@radix-ui/react-label'
@@ -9,33 +9,23 @@ import s from './checkbox.module.scss'
import { Check } from '@/assets/icons'
import { Typography } from '@/components'
export type CheckboxProps = {
className?: string
checked?: boolean
onChange?: (checked: boolean) => void
disabled?: boolean
required?: boolean
export type CheckboxProps = ComponentPropsWithoutRef<typeof CheckboxRadix.Root> & {
label?: string
id?: string
position?: 'left'
}
export const Checkbox: FC<CheckboxProps> = ({
checked,
onChange,
position,
disabled,
required,
label,
id,
className,
}) => {
export const Checkbox = forwardRef<ElementRef<typeof CheckboxRadix.Root>, CheckboxProps>(
({ position, label, className, ...rest }, ref) => {
const classNames = {
container: clsx(s.container, className),
buttonWrapper: clsx(s.buttonWrapper, disabled && s.disabled, position === 'left' && s.left),
buttonWrapper: clsx(
s.buttonWrapper,
rest.disabled && s.disabled,
position === 'left' && s.left
),
root: s.root,
indicator: s.indicator,
label: clsx(s.label, disabled && s.disabled),
label: clsx(s.label, rest.disabled && s.disabled),
}
return (
@@ -43,19 +33,10 @@ export const Checkbox: FC<CheckboxProps> = ({
<LabelRadix.Root asChild>
<Typography variant="body2" className={classNames.label} as={'label'}>
<div className={classNames.buttonWrapper}>
<CheckboxRadix.Root
className={classNames.root}
checked={checked}
onCheckedChange={onChange}
disabled={disabled}
required={required}
id={id}
>
{checked && (
<CheckboxRadix.Indicator className={classNames.indicator} forceMount>
<CheckboxRadix.Root className={classNames.root} {...rest} ref={ref}>
<CheckboxRadix.Indicator className={classNames.indicator}>
<Check />
</CheckboxRadix.Indicator>
)}
</CheckboxRadix.Root>
</div>
{label}
@@ -63,4 +44,7 @@ export const Checkbox: FC<CheckboxProps> = ({
</LabelRadix.Root>
</div>
)
}
}
)
Checkbox.displayName = 'Checkbox'

View File

@@ -0,0 +1,25 @@
import { FieldValues, useController, UseControllerProps } from 'react-hook-form'
import { Checkbox, CheckboxProps } from '@/components'
type Props<T extends FieldValues> = Omit<
UseControllerProps<T>,
'disabled' | 'rules' | 'defaultValue'
> &
Omit<CheckboxProps, 'checked' | 'onValueChange'>
export const ControlledCheckbox = <T extends FieldValues>({
control,
shouldUnregister,
...rest
}: Props<T>) => {
const {
field: { value, onChange, onBlur, ref },
} = useController({
name: rest.name,
control,
shouldUnregister,
disabled: rest.disabled,
})
return <Checkbox {...rest} checked={value} onCheckedChange={onChange} onBlur={onBlur} ref={ref} />
}

View File

@@ -0,0 +1,23 @@
import { FieldValues, useController, UseControllerProps } from 'react-hook-form'
import { TextField, TextFieldProps } from '@/components/ui/text-field'
type Props<T extends FieldValues> = Omit<
UseControllerProps<T>,
'disabled' | 'rules' | 'defaultValue'
> &
Omit<TextFieldProps, 'value' | 'onChange' | 'onValueChange'>
export const ControlledTextField = <T extends FieldValues>({
control,
shouldUnregister,
...rest
}: Props<T>) => {
const { field } = useController({
name: rest.name,
control,
shouldUnregister,
disabled: rest.disabled,
})
return <TextField {...rest} {...field} />
}