diff --git a/.axioma/quality.md b/.axioma/quality.md index 7494ad4..fe59e1f 100644 --- a/.axioma/quality.md +++ b/.axioma/quality.md @@ -89,3 +89,7 @@ ## 2024-06-30 - [Consistent AI Hook Error Recovery] **Learning:** AI hooks that implement async 'warmup' or cancellable operations must explicitly handle failures and AbortErrors to avoid "stuck" UI states. Transitioning status to 'idle' on AbortError and logging warmup failures while resetting status ensures the hook remains usable for subsequent interactions. **Action:** Always include AbortError handling in catch blocks and add error logging/state reset to warmup effects in all AI-related hooks. + +## 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. diff --git a/lib/components/Input/index.test.tsx b/lib/components/Input/index.test.tsx index 1adb561..d773f9e 100644 --- a/lib/components/Input/index.test.tsx +++ b/lib/components/Input/index.test.tsx @@ -205,4 +205,16 @@ describe('', () => { rerender() expect(input.value).toBe('updated') }) + + it('should handle input change without optional callbacks', () => { + const testId = 'test-no-callbacks' + const { getByTestId } = render( + , + ) + + const input = getByTestId(testId) + fireEvent.change(input, { target: { value: 'no callback' } }) + + expect((input as HTMLInputElement).value).toBe('no callback') + }) }) diff --git a/lib/components/Input/index.tsx b/lib/components/Input/index.tsx index b3e56e5..206d365 100644 --- a/lib/components/Input/index.tsx +++ b/lib/components/Input/index.tsx @@ -98,6 +98,15 @@ export const Input = forwardRef((props, ref) => { Input.displayName = 'Input' +/** + * Internal component to wrap the input with a label if provided. + * + * @param props - The props for the LabeledContainer component. + * @param props.label - The label to display. + * @param props.className - Optional CSS class for the label element. + * @param props.children - The input element to be wrapped. + * @returns The wrapped input or just the input if no label is provided. + */ const LabeledContainer = ({ label, className, @@ -116,6 +125,10 @@ const LabeledContainer = ({ children ) +/** + * Props for the Input component. + * Extends standard HTML input attributes. + */ export interface InputProps extends React.InputHTMLAttributes { /** @@ -129,13 +142,13 @@ export interface InputProps /** * Callback function triggered on value change and receives the current value of the input field. * - * @param {TData} value - The current value of the input field. + * @param value - The current value of the input field. */ onChangeValue?: (value: TData) => void /** * Callback function triggered on debounced value change. It receives the debounced value of the input field. * - * @param {TData} value - The debounced value of the input field. + * @param value - The debounced value of the input field. */ onChangeDebounce?: (value: TData) => void /** @@ -152,8 +165,8 @@ export interface InputProps /** * Custom transformation function for the input value. If provided, it will apply after the default transformation. * - * @param {string} value - The current value of the input field. - * @returns {string} - The transformed value of the input field. + * @param value - The current value of the input field. + * @returns The transformed value of the input field. */ transformFn?: (value: string) => string }