- Set up Tailwind CSS configuration for styling - Create Button component with variants (primary, secondary, outline, ghost, danger) - Create Input component with label, error, and helper text support - Create Card component with composable subcomponents (Header, Title, Description, Content, Footer) - Create LoadingSpinner component with size and fullscreen options - Create ErrorMessage component with retry and dismiss actions - Add comprehensive test suite using Jest and React Testing Library - Configure ESLint and Jest for quality assurance All components follow consistent design patterns from mobile app and include proper TypeScript types and accessibility features. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
98 lines
3.2 KiB
TypeScript
98 lines
3.2 KiB
TypeScript
import React from 'react';
|
|
import { render, screen } from '@testing-library/react';
|
|
import userEvent from '@testing-library/user-event';
|
|
import { Input } from '../Input';
|
|
|
|
describe('Input', () => {
|
|
it('renders input field correctly', () => {
|
|
render(<Input placeholder="Enter text" />);
|
|
expect(screen.getByPlaceholderText('Enter text')).toBeInTheDocument();
|
|
});
|
|
|
|
it('renders label when provided', () => {
|
|
render(<Input label="Email" placeholder="email@example.com" />);
|
|
expect(screen.getByText('Email')).toBeInTheDocument();
|
|
});
|
|
|
|
it('renders error message when error prop is provided', () => {
|
|
render(<Input error="This field is required" />);
|
|
expect(screen.getByText('This field is required')).toBeInTheDocument();
|
|
});
|
|
|
|
it('renders helper text when provided and no error', () => {
|
|
render(<Input helperText="Enter your email address" />);
|
|
expect(screen.getByText('Enter your email address')).toBeInTheDocument();
|
|
});
|
|
|
|
it('does not render helper text when error is present', () => {
|
|
render(
|
|
<Input
|
|
error="This field is required"
|
|
helperText="This should not be visible"
|
|
/>
|
|
);
|
|
expect(screen.queryByText('This should not be visible')).not.toBeInTheDocument();
|
|
});
|
|
|
|
it('applies error styling when error prop is provided', () => {
|
|
render(<Input error="Error" />);
|
|
const input = screen.getByRole('textbox');
|
|
expect(input).toHaveClass('border-error');
|
|
});
|
|
|
|
it('applies full width when specified', () => {
|
|
render(<Input fullWidth />);
|
|
const input = screen.getByRole('textbox');
|
|
expect(input).toHaveClass('w-full');
|
|
});
|
|
|
|
it('disables input when disabled prop is true', () => {
|
|
render(<Input disabled />);
|
|
const input = screen.getByRole('textbox');
|
|
expect(input).toBeDisabled();
|
|
});
|
|
|
|
it('accepts user input', async () => {
|
|
const user = userEvent.setup();
|
|
render(<Input placeholder="Type here" />);
|
|
const input = screen.getByPlaceholderText('Type here') as HTMLInputElement;
|
|
|
|
await user.type(input, 'Hello World');
|
|
expect(input.value).toBe('Hello World');
|
|
});
|
|
|
|
it('calls onChange when value changes', async () => {
|
|
const user = userEvent.setup();
|
|
const handleChange = jest.fn();
|
|
render(<Input onChange={handleChange} />);
|
|
const input = screen.getByRole('textbox');
|
|
|
|
await user.type(input, 'test');
|
|
expect(handleChange).toHaveBeenCalled();
|
|
});
|
|
|
|
it('applies custom className', () => {
|
|
render(<Input className="custom-input" />);
|
|
const input = screen.getByRole('textbox');
|
|
expect(input).toHaveClass('custom-input');
|
|
});
|
|
|
|
it('sets aria-invalid when error is present', () => {
|
|
render(<Input error="Error" id="test-input" />);
|
|
const input = screen.getByRole('textbox');
|
|
expect(input).toHaveAttribute('aria-invalid', 'true');
|
|
});
|
|
|
|
it('sets aria-describedby correctly for error', () => {
|
|
render(<Input error="Error message" id="test-input" />);
|
|
const input = screen.getByRole('textbox');
|
|
expect(input).toHaveAttribute('aria-describedby', 'test-input-error');
|
|
});
|
|
|
|
it('forwards ref correctly', () => {
|
|
const ref = React.createRef<HTMLInputElement>();
|
|
render(<Input ref={ref} />);
|
|
expect(ref.current).toBeInstanceOf(HTMLInputElement);
|
|
});
|
|
});
|