-
Notifications
You must be signed in to change notification settings - Fork 9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: made a new password input component #108
Changes from 6 commits
810becd
e7ea3aa
f69fd7f
77b7def
9f89baa
110608c
868a5ac
7603a3d
5c5959d
a8098e4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
@use '../variables'; | ||
|
||
$block: '.#{variables.$ns}password-input'; | ||
|
||
#{$block} { | ||
&::-ms-reveal, | ||
&::-ms-clear { | ||
display: none; | ||
} | ||
|
||
&__additional-right-content { | ||
display: flex; | ||
align-items: center; | ||
} | ||
|
||
&__copy-button { | ||
margin-right: 4px; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
import React from 'react'; | ||
|
||
import {Eye, EyeSlash} from '@gravity-ui/icons'; | ||
import {Button, ClipboardButton, TextInput, TextInputProps, Tooltip} from '@gravity-ui/uikit'; | ||
|
||
import {block} from '../utils/cn'; | ||
|
||
import i18n from './i18n'; | ||
|
||
import './PasswordInput.scss'; | ||
|
||
const b = block('password-input'); | ||
|
||
export type PasswordInputProps = Required<Pick<TextInputProps, 'onUpdate' | 'value'>> & | ||
Omit<TextInputProps, 'type'> & { | ||
/** Show copy button */ | ||
showCopyButton?: boolean; | ||
/** Show reveal button */ | ||
showRevealButton?: boolean; | ||
/** Disable tooltip. Tooltip won't be shown */ | ||
hasTooltip?: boolean; | ||
}; | ||
|
||
export const PasswordInput: React.FC<PasswordInputProps> = (props) => { | ||
const { | ||
autoComplete, | ||
value, | ||
showCopyButton, | ||
rightContent, | ||
showRevealButton, | ||
size = 'm', | ||
hasTooltip = true, | ||
} = props; | ||
|
||
const [hideValue, setHideValue] = React.useState(true); | ||
|
||
const additionalRightContent = React.useMemo(() => { | ||
if (!showRevealButton && !showCopyButton) { | ||
return <React.Fragment>{rightContent}</React.Fragment>; | ||
} | ||
|
||
const onClick = () => { | ||
setHideValue((hideValue) => !hideValue); | ||
}; | ||
|
||
return ( | ||
<div className={b('additional-right-content')}> | ||
{rightContent} | ||
{value && showCopyButton ? ( | ||
<ClipboardButton | ||
text={value} | ||
hasTooltip={hasTooltip} | ||
size={16} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should map input size to button size |
||
className={b('copy-button')} | ||
/> | ||
) : null} | ||
{showRevealButton ? ( | ||
<Tooltip | ||
disabled={!hasTooltip} | ||
content={ | ||
hideValue ? i18n('label_show-password') : i18n('label_hide-password') | ||
} | ||
> | ||
<Button | ||
view="flat-secondary" | ||
onClick={onClick} | ||
size={size} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TextInput size !== Button size, take a look at clear button, maybe reuse this logic here as well |
||
extraProps={{ | ||
'aria-label': hideValue | ||
? i18n('label_show-password') | ||
: i18n('label_hide-password'), | ||
}} | ||
> | ||
<Button.Icon>{hideValue ? <Eye /> : <EyeSlash />}</Button.Icon> | ||
</Button> | ||
</Tooltip> | ||
) : null} | ||
</div> | ||
); | ||
}, [showRevealButton, showCopyButton, rightContent, value, hasTooltip, hideValue, size]); | ||
|
||
return ( | ||
<TextInput | ||
{...props} | ||
type={hideValue ? 'password' : 'text'} | ||
rightContent={additionalRightContent} | ||
autoComplete={autoComplete ? autoComplete : 'new-password'} | ||
controlProps={{ | ||
className: b(), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Now it's an opposite, you're passing root className ( |
||
}} | ||
/> | ||
); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
## PasswordInput | ||
|
||
Password Input display component | ||
|
||
### PropTypes | ||
|
||
Same as [TextInput component](https://github.com/gravity-ui/uikit/blob/main/src/components/controls/TextInput/README.md), with some exceptions: | ||
|
||
- `value` is required property; | ||
- `onUpdate` is required property; | ||
- `type` is omitted; | ||
|
||
| Property | Type | Required | Default | Description | | ||
| :--------------- | :-------- | :------- | :------ | :-------------------------------------- | | ||
| showCopyButton | `boolean` | | | Show copy button | | ||
| showRevealButton | `boolean` | | | Show reveal button | | ||
| hasTooltip | `boolean` | | `true` | Disable tooltip. Tooltip won't be shown | | ||
|
||
#### Usage example | ||
|
||
```jsx harmony | ||
function MyComponent() { | ||
const [value, setValue] = React.useState(''); | ||
|
||
return ( | ||
<PasswordInput | ||
showCopyButton={true} | ||
showVisibilityButton={true} | ||
onUpdate={setValue} | ||
value={value} | ||
/> | ||
); | ||
} | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import React from 'react'; | ||
|
||
import {Button} from '@gravity-ui/uikit'; | ||
import type {Meta, StoryFn} from '@storybook/react'; | ||
|
||
import {cn} from '../../utils/cn'; | ||
import {PasswordInput} from '../PasswordInput'; | ||
|
||
import './PasswordInputStories.scss'; | ||
|
||
const b = cn('password-input-stories'); | ||
|
||
export default { | ||
title: 'Components/PasswordInput', | ||
component: PasswordInput, | ||
} as Meta; | ||
|
||
const DefaultTemplate: StoryFn<React.ComponentProps<typeof PasswordInput>> = () => { | ||
const [value, setValue] = React.useState(''); | ||
|
||
return ( | ||
<PasswordInput | ||
showCopyButton={true} | ||
showRevealButton={true} | ||
onUpdate={setValue} | ||
value={value} | ||
/> | ||
); | ||
}; | ||
|
||
export const Default = DefaultTemplate.bind({}); | ||
|
||
const WithGenerateRandomValueTemplate: StoryFn<React.ComponentProps<typeof PasswordInput>> = ( | ||
props: React.ComponentProps<typeof PasswordInput>, | ||
) => { | ||
const [value, setValue] = React.useState(''); | ||
|
||
const generateRandomValue = React.useCallback(() => { | ||
let randomValue = ''; | ||
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; | ||
const charactersLength = characters.length; | ||
let counter = 0; | ||
|
||
while (counter < charactersLength) { | ||
randomValue += characters.charAt(Math.floor(Math.random() * charactersLength)); | ||
counter += 1; | ||
} | ||
|
||
setValue(randomValue); | ||
}, []); | ||
|
||
return ( | ||
<div className={b()}> | ||
<PasswordInput {...props} onUpdate={setValue} value={value} /> | ||
<Button onClick={generateRandomValue} className={b('button-generate-random-value')}> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you add prop |
||
Generate random value | ||
</Button> | ||
</div> | ||
); | ||
}; | ||
|
||
export const WithGenerateRandomValue = WithGenerateRandomValueTemplate.bind({}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
.password-input-stories { | ||
display: flex; | ||
|
||
&__button-generate-random-value { | ||
margin-left: 8px; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"label_show-password": "Show password", | ||
"label_hide-password": "Hide password" | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import {registerKeyset} from '../../utils/registerKeyset'; | ||
|
||
import en from './en.json'; | ||
import ru from './ru.json'; | ||
|
||
const COMPONENT = 'PasswordInput'; | ||
|
||
export default registerKeyset({en, ru}, COMPONENT); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"label_show-password": "Показать пароль", | ||
"label_hide-password": "Скрыть пароль" | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './PasswordInput'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would pick separate names here,
hasCopyTooltip
andhasRevealTooltip