Skip to content

Commit

Permalink
fix: fixed issue where Field would not infer correct change handler
Browse files Browse the repository at this point in the history
  • Loading branch information
Pagebakers committed Oct 18, 2023
1 parent 5a55000 commit 654ad18
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 8 deletions.
6 changes: 6 additions & 0 deletions .changeset/red-needles-promise.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@saas-ui/forms': patch
'@saas-ui/react': patch
---

Fixed issue where Field would not infer correct onChange handler
10 changes: 7 additions & 3 deletions packages/saas-ui-forms/src/create-field.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ const withControlledInput = (InputComponent: React.FC<any>) => {
({ name, rules, ...inputProps }, ref) => {
const { control } = useFormContext()

const onChange = inputProps.onChange as (...event: any[]) => void

return (
<Controller
name={name}
Expand All @@ -79,7 +81,7 @@ const withControlledInput = (InputComponent: React.FC<any>) => {
<InputComponent
{...field}
{...inputProps}
onChange={callAllHandlers(inputProps.onChange, field.onChange)}
onChange={callAllHandlers(onChange, field.onChange)}
onBlur={callAllHandlers(inputProps.onBlur, field.onBlur)}
ref={useMergeRefs(ref, _ref)}
/>
Expand All @@ -97,11 +99,13 @@ const withUncontrolledInput = (InputComponent: React.FC<any>) => {

const { ref: _ref, ...field } = register(name, rules)

const onChange = inputProps.onChange as (...event: any[]) => void

return (
<InputComponent
{...field}
{...inputProps}
onChange={callAllHandlers(inputProps.onChange, field.onChange)}
onChange={callAllHandlers(onChange, field.onChange)}
onBlur={callAllHandlers(inputProps.onBlur, field.onBlur)}
ref={useMergeRefs(ref, _ref)}
/>
Expand Down Expand Up @@ -139,7 +143,7 @@ export const createField = <TProps extends object>(
displayName: `${component.displayName ?? 'Custom'}Field`,
hideLabel: options?.hideLabel,
BaseField: options?.BaseField || BaseField,
}) as React.FC<TProps & BaseFieldProps>
}) as React.FC<Omit<BaseFieldProps, keyof TProps> & TProps>

return Field
}
16 changes: 12 additions & 4 deletions packages/saas-ui-forms/src/default-fields.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import {
SelectProps,
NativeSelect,
NativeSelectProps,
SelectButtonProps,
SelectListProps,
} from './select'

import { createField } from './create-field'
Expand Down Expand Up @@ -83,12 +85,18 @@ export const SwitchField = createField<SwitchProps>(
}
)

export const SelectField = createField<SelectProps>(
export interface SelectFieldProps extends SelectProps {
buttonProps?: SelectButtonProps
listProps?: SelectListProps
}

export const SelectField = createField<SelectFieldProps>(
forwardRef((props, ref) => {
const { buttonProps, listProps, ...rest } = props
return (
<Select ref={ref} {...props}>
<SelectButton />
<SelectList />
<Select ref={ref} {...rest}>
<SelectButton {...buttonProps} />
<SelectList {...listProps} />
</Select>
)
}),
Expand Down
2 changes: 1 addition & 1 deletion packages/saas-ui-forms/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export type ArrayFieldPath<Name extends string> = Name extends string
export interface BaseFieldProps<
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
> extends Omit<FormControlProps, 'label' | 'type'> {
> extends Omit<FormControlProps, 'label' | 'type' | 'onChange'> {
/**
* The field name
*/
Expand Down
56 changes: 56 additions & 0 deletions packages/saas-ui-forms/tests/create-field.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import * as React from 'react'

import {
DefaultFields,
Field,
FieldProps,
Select,
SelectButton,
SelectList,
SelectProps,
createField,
} from '../src'
import { forwardRef } from '@chakra-ui/react'
import { expectTypeOf } from 'vitest'

export const CustomField = createField<SelectProps>(
forwardRef((props, ref) => {
return (
<Select ref={ref} {...props}>
<SelectButton />
<SelectList />
</Select>
)
}),
{
isControlled: true,
}
)

type PropTypes = React.ComponentProps<typeof CustomField>

test('should have correct event handler type', async () => {
expectTypeOf<PropTypes['onChange']>().toEqualTypeOf<
((value: string | string[]) => void) | undefined
>()
})

const selectProps: FieldProps = {
name: 'test',
type: 'select',
}

const inputProps: FieldProps = {
name: 'test',
type: 'text',
}

test('should have correct event handler type on Field', async () => {
expectTypeOf<(typeof selectProps)['onChange']>().toEqualTypeOf<
((value: string | string[]) => void) | undefined
>()

expectTypeOf<(typeof inputProps)['onChange']>().toEqualTypeOf<
React.ChangeEventHandler<HTMLInputElement> | undefined
>()
})

0 comments on commit 654ad18

Please sign in to comment.