Skip to content

Commit

Permalink
feat: add accessibility props
Browse files Browse the repository at this point in the history
- Added aria-label, tabIndex and ariaLabelledBy to Input.tsx and Input.types.d.ts.
- Added accessibility tests in input test suite.
  • Loading branch information
ishaan000 committed Sep 24, 2024
1 parent 519a4ff commit 7e86a46
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 96 deletions.
12 changes: 12 additions & 0 deletions src/components/atoms/Input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ const Input: React.FC<InputProps> = ({
multiline,
rows,
sx,
ariaLabel,
ariaLabelledBy,
tabIndex,
...props
}: InputProps) => {
return (
Expand All @@ -32,6 +35,15 @@ const Input: React.FC<InputProps> = ({
multiline={multiline}

Check warning on line 35 in src/components/atoms/Input.tsx

View workflow job for this annotation

GitHub Actions / build

Props should be sorted alphabetically
rows={rows}
sx={sx}
slotProps={{
input: {
'aria-label': ariaLabel,
'aria-labelledby': ariaLabelledBy,
},
htmlInput: {
tabIndex: tabIndex,
},
}}
{...props}
/>
);
Expand Down
44 changes: 24 additions & 20 deletions src/types/Input.types.d.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
declare module '@components/atoms/Input' {
import { FC, ChangeEventHandler } from 'react';
import { TextFieldProps as MuiInputProps } from '@mui/material/TextField';

export interface InputProps extends Omit<MuiInputProps, 'variant'> {
label: string;
type?: string;
value?: string | number;
variant?: 'filled' | 'outlined' | 'standard';
placeholder?: string;
helperText?: string;
error?: boolean;
disabled?: boolean;
multiline?: boolean;
rows?: number;
sx?: object;
}

const Input: FC<InputProps>;
export default Input;
}
import { FC, ChangeEventHandler } from 'react';
import { TextFieldProps as MuiInputProps } from '@mui/material/TextField';

export interface InputProps extends Omit<MuiInputProps, 'variant' | 'onChange'> {

Check failure on line 5 in src/types/Input.types.d.ts

View workflow job for this annotation

GitHub Actions / build

Insert `⏎···`
label: string;
type?: string;
onChange?: ChangeEventHandler<HTMLInputElement | HTMLTextAreaElement>;
value?: string | number;
variant?: 'filled' | 'outlined' | 'standard';
placeholder?: string;
helperText?: string;
error?: boolean;
disabled?: boolean;
multiline?: boolean;
rows?: number;
sx?: object;
tabIndex?: number;
ariaLabel?: string;
ariaLabelledBy?: string;
}

const Input: FC<InputProps>;
export default Input;
}

Check failure on line 25 in src/types/Input.types.d.ts

View workflow job for this annotation

GitHub Actions / build

Insert `⏎`
111 changes: 35 additions & 76 deletions tests/components/atoms/Input.test.tsx
Original file line number Diff line number Diff line change
@@ -1,112 +1,71 @@
import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import Input from '../../../src/components/atoms/Input';
import { TextField as MuiInput } from '@mui/material';
import '@testing-library/jest-dom';

jest.mock('@mui/material', () => ({
TextField: jest.fn().mockImplementation(({ label, helperText, error, ...props }) => (
<>
<input aria-label={label} {...props} />
{helperText && <span aria-label="helperText">{helperText}</span>}
{error && <span aria-label="error">Error</span>}
</>
)),
}));

describe('Input component', () => {
const defaultProps = {
label: 'Test Input',
variant: 'outlined' as const,
value: '',
onChange: jest.fn(),
placeholder: 'Enter value',
};

beforeEach(() => {
(MuiInput as jest.Mock).mockClear();
});

afterEach(() => {
jest.clearAllMocks();
});

it('should render the input with the correct label', () => {
render(<Input {...defaultProps} />);
expect(screen.getByLabelText('Test Input')).toBeInTheDocument();
});

it('should pass the correct variant prop to MuiInput', () => {
render(<Input {...defaultProps} variant="filled" />);
expect(MuiInput).toHaveBeenCalledWith(
expect.objectContaining({ variant: 'filled' }),
expect.anything()
);
it('should display the correct value', () => {
render(<Input {...defaultProps} value="Test Value" />);
const input = screen.getByLabelText('Test Input') as HTMLInputElement;
expect(input.value).toBe('Test Value');
});

it('should call onChange when the input value changes', () => {
render(<Input {...defaultProps} />);
const input = screen.getByLabelText('Test Input');
fireEvent.change(input, { target: { value: 'new value' } });
const input = screen.getByLabelText('Test Input') as HTMLInputElement;
fireEvent.change(input, { target: { value: 'New Value' } });
expect(defaultProps.onChange).toHaveBeenCalledTimes(1);
expect(defaultProps.onChange).toHaveBeenCalledWith(expect.any(Object));
});

it('should pass the correct type prop to MuiInput', () => {
render(<Input {...defaultProps} type="password" />);
expect(MuiInput).toHaveBeenCalledWith(
expect.objectContaining({ type: 'password' }),
expect.anything()
);
});

it('should use default values when no props are provided', () => {
render(<Input label="Default Input" />);
expect(MuiInput).toHaveBeenCalledWith(
expect.objectContaining({
variant: 'outlined',
type: 'text',
}),
expect.anything()
);
expect(screen.getByLabelText('Default Input')).toBeInTheDocument();
});

it('should pass additional props to MuiInput', () => {
render(<Input {...defaultProps} disabled />);
expect(MuiInput).toHaveBeenCalledWith(
expect.objectContaining({ disabled: true }),
expect.anything()
);
it('should render the placeholder text correctly', () => {
render(<Input {...defaultProps} placeholder="Enter your username" />);
const input = screen.getByPlaceholderText('Enter your username');
expect(input).toBeInTheDocument();
});

it('should render the placeholder text', () => {
render(<Input {...defaultProps} placeholder="Enter your name" />);
expect(screen.getByPlaceholderText('Enter your name')).toBeInTheDocument();
it('should apply the correct aria-label for accessibility', () => {
render(<Input {...defaultProps} ariaLabel="Test Aria Label" />);
const input = screen.getByLabelText('Test Aria Label');
expect(input).toBeInTheDocument();
});

it('should render helper text when provided', () => {
render(<Input {...defaultProps} helperText="This is helper text" />);
expect(screen.getByLabelText('helperText')).toHaveTextContent('This is helper text');
it('should apply the correct tabIndex', () => {
render(<Input {...defaultProps} tabIndex={3} />);
const input = screen.getByLabelText('Test Input');
expect(input).toHaveAttribute('tabIndex', '3');
});

it('should show an error when the error prop is true', () => {
render(<Input {...defaultProps} error />);
expect(screen.getByLabelText('error')).toHaveTextContent('Error');
it('should render the input as disabled when disabled prop is true', () => {
render(<Input {...defaultProps} disabled />);
const input = screen.getByLabelText('Test Input') as HTMLInputElement;
expect(input.disabled).toBe(true);
});

it('should handle multiline input', () => {
render(<Input {...defaultProps} multiline rows={4} />);
expect(MuiInput).toHaveBeenCalledWith(
expect.objectContaining({ multiline: true, rows: 4 }),
expect.anything()
);
it('should render the input with an error state when error prop is true', () => {
render(<Input {...defaultProps} error helperText="This field is required" />);

Check failure on line 58 in tests/components/atoms/Input.test.tsx

View workflow job for this annotation

GitHub Actions / build

Replace `<Input·{...defaultProps}·error·helperText="This·field·is·required"·/>` with `⏎······<Input·{...defaultProps}·error·helperText="This·field·is·required"·/>⏎····`
const input = screen.getByLabelText('Test Input');
expect(input).toBeInTheDocument();
const errorMessage = screen.getByText('This field is required');
expect(errorMessage).toBeInTheDocument();
});

it('should pass the sx prop for custom styling', () => {
const sx = { backgroundColor: 'lightgray' };
render(<Input {...defaultProps} sx={sx} />);
expect(MuiInput).toHaveBeenCalledWith(
expect.objectContaining({ sx }),
expect.anything()
);
it('should render as multiline with the correct number of rows when multiline prop is true', () => {
render(<Input {...defaultProps} multiline rows={3} />);
const input = screen.getByLabelText('Test Input') as HTMLTextAreaElement;
expect(input.tagName.toLowerCase()).toBe('textarea');
expect(input.rows).toBe(3);
});
});

Check failure on line 71 in tests/components/atoms/Input.test.tsx

View workflow job for this annotation

GitHub Actions / build

Insert `⏎`

0 comments on commit 7e86a46

Please sign in to comment.