Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .axioma/quality.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,7 @@
## 2024-07-01 - [Achieving 100% Branch Coverage for Optional Callbacks]
**Learning:** To achieve 100% branch coverage in components that utilize optional callbacks (e.g., `Input`'s `onChangeValue`, `onChangeDebounce`), unit tests must trigger relevant events (like `fireEvent.change`) while intentionally omitting these optional props to exercise the 'falsy' branches of conditional logic.
**Action:** Always include a test case that omits optional callback props when testing components with event handlers to ensure all logical paths are verified.

## 2024-07-05 - [Ensuring Internal Event Handlers Precedence]
**Learning:** When creating wrapper components that spread `restProps` onto a native element, placing internal event handlers (like `onSubmit` or `onChange`) *after* the spread ensures they are not accidentally overwritten by user-provided props. Additionally, destructuring the specific handler from props allows it to be manually invoked within the internal handler, preserving both functionalities.
**Action:** Always spread `restProps` before specifying internal event handlers in wrapper components to maintain control over the component's core logic while still supporting custom callbacks.
24 changes: 24 additions & 0 deletions lib/components/Form/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -212,4 +212,28 @@ describe('Form Component', () => {
expect(ref.current).not.toBeNull()
expect(ref.current?.tagName).toBe('FORM')
})

it('should call both onSubmitValues and onSubmit when both are provided', () => {
const onSubmitValues = vi.fn()
const onSubmit = vi.fn()
const testId = 'test-both-submits'

render(
<Form<TestFormValues>
onSubmitValues={onSubmitValues}
onSubmit={onSubmit}
data-testid={testId}
>
<input name="name" defaultValue="John" />
<button type="submit">Submit</button>
</Form>,
)

fireEvent.submit(screen.getByTestId(testId))

expect(onSubmitValues).toHaveBeenCalledWith({
name: 'John',
})
expect(onSubmit).toHaveBeenCalled()
})
})
22 changes: 14 additions & 8 deletions lib/components/Form/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ const FormInner = <T,>(
props: FormProps<T>,
ref: ForwardedRef<HTMLFormElement>,
): JSX.Element => {
const { onSubmitValues, filterEmptyValues, ...restProps } = props
const {
onSubmitValues,
filterEmptyValues,
onSubmit: onSubmitProp,
...restProps
} = props

/**
* Handles the form submission event.
Expand All @@ -31,9 +36,9 @@ const FormInner = <T,>(

onSubmitValues(values)
}
props.onSubmit && props.onSubmit(event)
onSubmitProp && onSubmitProp(event)
}
return <form ref={ref} onSubmit={onSubmit} {...restProps} />
return <form ref={ref} {...restProps} onSubmit={onSubmit} />
}

/**
Expand Down Expand Up @@ -69,9 +74,12 @@ const FormInner = <T,>(
* </Form>
* ```
*/
export const Form = forwardRef(FormInner) as <T>(
props: FormProps<T> & React.RefAttributes<HTMLFormElement>,
) => JSX.Element
export const Form = forwardRef(FormInner) as {
<T>(props: FormProps<T> & React.RefAttributes<HTMLFormElement>): JSX.Element
displayName?: string
}

Form.displayName = 'Form'

/**
* The properties for the Form component.
Expand Down Expand Up @@ -99,8 +107,6 @@ export interface FormProps<T>
onSubmitValues?: (values: T) => void
/**
* A flag to determine if empty values should be included in the form values.
*
* @param {boolean} filterEmptyValues - A flag to determine if empty values should be included in the form values.
*/
filterEmptyValues?: boolean
}
Loading