-
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 4 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,13 @@ | ||
@use '../variables'; | ||
|
||
$block: '.#{variables.$ns}password-input'; | ||
|
||
#{$block} { | ||
&__additional-right-content { | ||
display: flex; | ||
} | ||
|
||
&__button { | ||
--yc-button-background-color-hover: transparent; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
import React from 'react'; | ||
|
||
import {Copy, CopyCheck, Eye, EyeSlash} from '@gravity-ui/icons'; | ||
import { | ||
Button, | ||
CopyToClipboard, | ||
CopyToClipboardStatus, | ||
Icon, | ||
TextInput, | ||
TextInputProps, | ||
} from '@gravity-ui/uikit'; | ||
|
||
import {block} from '../utils/cn'; | ||
|
||
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 visibility button */ | ||
showVisibilityButton?: boolean; | ||
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. Lets call it |
||
}; | ||
|
||
export const PasswordInput: React.FC<PasswordInputProps> = (props) => { | ||
const {autoComplete, value, showCopyButton, rightContent, showVisibilityButton} = props; | ||
|
||
const [hideValue, setHideValue] = React.useState(true); | ||
|
||
const additionalRightContent = React.useMemo(() => { | ||
if (!showVisibilityButton && !showCopyButton) { | ||
return <React.Fragment>{rightContent}</React.Fragment>; | ||
} | ||
|
||
const onClick = () => { | ||
setHideValue((hideValue) => !hideValue); | ||
}; | ||
|
||
return ( | ||
<div className={b('additional-right-content')}> | ||
{rightContent} | ||
{value && showCopyButton ? ( | ||
<CopyToClipboard text={String(value)} timeout={500}> | ||
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 be replaced with |
||
{(state) => ( | ||
<Button view="flat-secondary" className={b('button')} size="s"> | ||
<Icon | ||
size={14} | ||
data={ | ||
state === CopyToClipboardStatus.Pending ? Copy : CopyCheck | ||
} | ||
/> | ||
</Button> | ||
)} | ||
</CopyToClipboard> | ||
) : null} | ||
{showVisibilityButton ? ( | ||
<Button | ||
view="flat-secondary" | ||
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. aria-label prop would be nice here with the texts: |
||
onClick={onClick} | ||
className={b('button')} | ||
size="s" | ||
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 use different sizes for each TextInput size |
||
> | ||
<Icon data={hideValue ? Eye : EyeSlash} size={14} /> | ||
</Button> | ||
) : null} | ||
</div> | ||
); | ||
}, [rightContent, value, showCopyButton, showVisibilityButton, hideValue]); | ||
|
||
return ( | ||
<TextInput | ||
{...props} | ||
type={hideValue ? 'password' : 'text'} | ||
rightContent={additionalRightContent} | ||
autoComplete={autoComplete ? autoComplete : 'new-password'} | ||
/> | ||
); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
## 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 | | ||
| showVisibilityButton | `boolean` | | | Show visibility button | | ||
|
||
#### 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} | ||
showVisibilityButton={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 @@ | ||
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.
Technically this is not part of public API. Moreover this var will be removed in the next major