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 @@ -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.
12 changes: 12 additions & 0 deletions lib/components/Input/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -205,4 +205,16 @@ describe('<Input />', () => {
rerender(<Input value="updated" data-testid="controlled-input" />)
expect(input.value).toBe('updated')
})

it('should handle input change without optional callbacks', () => {
const testId = 'test-no-callbacks'
const { getByTestId } = render(
<Input id={testId} data-testid={testId} />,
)

const input = getByTestId(testId)
fireEvent.change(input, { target: { value: 'no callback' } })

expect((input as HTMLInputElement).value).toBe('no callback')
})
})
21 changes: 17 additions & 4 deletions lib/components/Input/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,15 @@ export const Input = forwardRef<HTMLInputElement, InputProps>((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,
Expand All @@ -116,6 +125,10 @@ const LabeledContainer = ({
children
)

/**
* Props for the Input component.
* Extends standard HTML input attributes.
*/
export interface InputProps
extends React.InputHTMLAttributes<HTMLInputElement> {
/**
Expand All @@ -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
/**
Expand All @@ -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
}
Expand Down
Loading