Skip to content

Commit 140f1d3

Browse files
authored
feat(ui): add accessible show/hide password toggle on signin (#6098)
1 parent 5f85387 commit 140f1d3

2 files changed

Lines changed: 49 additions & 3 deletions

File tree

packages/ui/src/ui-component/input/Input.jsx

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import { useState, useEffect, useRef } from 'react'
22
import PropTypes from 'prop-types'
3-
import { FormControl, OutlinedInput, InputBase, Popover } from '@mui/material'
3+
import { FormControl, OutlinedInput, InputBase, Popover, InputAdornment, IconButton } from '@mui/material'
44
import { useTheme } from '@mui/material/styles'
5+
import Visibility from '@mui/icons-material/Visibility'
6+
import VisibilityOff from '@mui/icons-material/VisibilityOff'
57
import SelectVariable from '@/ui-component/json/SelectVariable'
68
import { getAvailableNodesForVariable } from '@/utils/genericHelper'
79

@@ -10,7 +12,13 @@ export const Input = ({ inputParam, value, nodes, edges, nodeId, onChange, onBlu
1012
const [myValue, setMyValue] = useState(value ?? '')
1113
const [anchorEl, setAnchorEl] = useState(null)
1214
const [availableNodesForVariable, setAvailableNodesForVariable] = useState([])
15+
const [isPasswordVisible, setIsPasswordVisible] = useState(false)
1316
const ref = useRef(null)
17+
const inputElementRef = useRef(null)
18+
const selectionRangeRef = useRef({ start: null, end: null })
19+
20+
const isPasswordField = inputParam?.type === 'password'
21+
const hasPasswordToggle = isPasswordField && !!inputParam?.enablePasswordToggle
1422

1523
const openPopOver = Boolean(anchorEl)
1624

@@ -52,6 +60,28 @@ export const Input = ({ inputParam, value, nodes, edges, nodeId, onChange, onBlu
5260
}
5361
}, [myValue])
5462

63+
useEffect(() => {
64+
if (!hasPasswordToggle) return
65+
const { start, end } = selectionRangeRef.current
66+
if (start === null || end === null || !inputElementRef.current) return
67+
68+
requestAnimationFrame(() => {
69+
inputElementRef.current?.focus()
70+
inputElementRef.current?.setSelectionRange(start, end)
71+
})
72+
}, [hasPasswordToggle, isPasswordVisible])
73+
74+
const handleTogglePasswordVisibility = () => {
75+
const inputElement = inputElementRef.current
76+
if (inputElement) {
77+
selectionRangeRef.current = {
78+
start: inputElement.selectionStart,
79+
end: inputElement.selectionEnd
80+
}
81+
}
82+
setIsPasswordVisible((prev) => !prev)
83+
}
84+
5585
return (
5686
<>
5787
{inputParam.name === 'note' ? (
@@ -99,12 +129,13 @@ export const Input = ({ inputParam, value, nodes, edges, nodeId, onChange, onBlu
99129
id={inputParam.name}
100130
size='small'
101131
disabled={disabled}
102-
type={getInputType(inputParam.type)}
132+
type={hasPasswordToggle ? (isPasswordVisible ? 'text' : 'password') : getInputType(inputParam.type)}
103133
placeholder={inputParam.placeholder}
104134
multiline={!!inputParam.rows}
105135
rows={inputParam.rows ?? 1}
106136
value={myValue}
107137
name={inputParam.name}
138+
inputRef={inputElementRef}
108139
onChange={(e) => {
109140
setMyValue(e.target.value)
110141
onChange(e.target.value)
@@ -118,6 +149,20 @@ export const Input = ({ inputParam, value, nodes, edges, nodeId, onChange, onBlu
118149
height: inputParam.rows ? '90px' : 'inherit'
119150
}
120151
}}
152+
endAdornment={
153+
hasPasswordToggle ? (
154+
<InputAdornment position='end'>
155+
<IconButton
156+
edge='end'
157+
onClick={handleTogglePasswordVisibility}
158+
onMouseDown={(e) => e.preventDefault()}
159+
aria-label={isPasswordVisible ? 'Hide password' : 'Show password'}
160+
>
161+
{isPasswordVisible ? <VisibilityOff fontSize='small' /> : <Visibility fontSize='small' />}
162+
</IconButton>
163+
</InputAdornment>
164+
) : undefined
165+
}
121166
sx={{
122167
'& .MuiOutlinedInput-notchedOutline': {
123168
borderColor: theme.palette.grey[900] + 25

packages/ui/src/views/auth/signIn.jsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ const SignInPage = () => {
5353
label: 'Password',
5454
name: 'password',
5555
type: 'password',
56-
placeholder: '********'
56+
placeholder: '********',
57+
enablePasswordToggle: true
5758
}
5859
const [usernameVal, setUsernameVal] = useState('')
5960
const [passwordVal, setPasswordVal] = useState('')

0 commit comments

Comments
 (0)