Skip to content

Commit

Permalink
feat(Input): add input visibility toggle (#37)
Browse files Browse the repository at this point in the history
* feat(Input): add input visibility toggle
- Implement `TextField` `endAdorment` for password fields.
- Add `showPasswordToggle` prop.
- Add `@mui/icons-material` to devDependencies.
- Integrate `Visibility`/`VisibilityOff` MUI icons.
- Create test for password toggle feature.
- Validate visibility toggle button behavior for password inputs.

* Apply suggestions from code review
Co-authored-by: Ryan James Meneses <[email protected]>

* fix: remove `handleMouseDownShowInput` and refactor tests

---------

Co-authored-by: Ryan James Meneses <[email protected]>
  • Loading branch information
ishaan000 and hiyaryan committed Jan 23, 2025
1 parent 53d9b9c commit 29980de
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 24 deletions.
72 changes: 50 additions & 22 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"@babel/preset-env": "^7.25.4",
"@babel/preset-react": "^7.24.7",
"@babel/preset-typescript": "^7.24.7",
"@mui/icons-material": "^6.4.1",
"@stylistic/eslint-plugin-ts": "^2.13.0",
"@testing-library/jest-dom": "^6.5.0",
"@testing-library/react": "^16.0.1",
Expand Down
24 changes: 22 additions & 2 deletions src/components/atoms/Input.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
'use client';

import { IconButton, InputAdornment } from '@mui/material';
import React, { useState } from 'react';
import { Visibility, VisibilityOff } from '@mui/icons-material';
import { InputProps } from '@components/atoms/Input';
import { TextField as MuiInput } from '@mui/material';
import React from 'react';

const Input: React.FC<InputProps> = ({
label,
Expand All @@ -20,8 +22,15 @@ const Input: React.FC<InputProps> = ({
ariaLabel,
ariaLabelledBy,
tabIndex,
hideInput = false,
...props
}: InputProps) => {
const [showInput, setShowInput] = useState(false);

const handleClickShowInput = () => {
setShowInput(!showInput);
};

return (
<MuiInput
disabled={disabled}
Expand All @@ -36,13 +45,24 @@ const Input: React.FC<InputProps> = ({
input: {
'aria-label': ariaLabel,
'aria-labelledby': ariaLabelledBy,
endAdornment: hideInput ? (
<InputAdornment position="end">
<IconButton
aria-label="toggle input visibility"
onClick={handleClickShowInput}
edge="end"
>
{showInput ? <Visibility /> : <VisibilityOff />}{' '}
</IconButton>
</InputAdornment>
) : undefined,
},
htmlInput: {
tabIndex: tabIndex,
},
}}
sx={sx}
type={type}
type={showInput ? 'text' : type}
value={value}
variant={variant}
{...props}
Expand Down
1 change: 1 addition & 0 deletions src/types/Input.types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ declare module '@components/atoms/Input' {
tabIndex?: number;
ariaLabel?: string;
ariaLabelledBy?: string;
hideInput?: boolean;
}

const Input: FC<InputProps>;
Expand Down
24 changes: 24 additions & 0 deletions tests/components/atoms/Input.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,28 @@ describe('Input component', () => {
expect(input.tagName.toLowerCase()).toBe('textarea');
expect(input.rows).toBe(3);
});

it('should show an input toggle button when hideInput is true', () => {
render(<Input {...defaultProps} type="password" hideInput />);
const input = screen.getByLabelText('Test Input') as HTMLInputElement;
expect(input.type).toBe('password');

const inputToggle = screen.getByLabelText('toggle input visibility');
expect(inputToggle).toBeInTheDocument();

fireEvent.click(inputToggle);
expect(input.type).toBe('text');

fireEvent.click(inputToggle);
expect(input.type).toBe('password');
});

it('should not show an input toggle button when hideInput is false', () => {
render(<Input {...defaultProps} type="password" hideInput={false} />);
const input = screen.getByLabelText('Test Input') as HTMLInputElement;
expect(input.type).toBe('password');

const inputToggle = screen.queryByLabelText('toggle input visibility');
expect(inputToggle).not.toBeInTheDocument();
});
});

0 comments on commit 29980de

Please sign in to comment.