2019-09-05 07:20:12 +00:00
|
|
|
import React from 'react'
|
|
|
|
import styled from 'styled-components'
|
|
|
|
import PropTypes from 'prop-types'
|
2019-09-12 10:58:17 +00:00
|
|
|
import isEqual from "lodash/isEqual";
|
2019-09-05 07:20:12 +00:00
|
|
|
|
|
|
|
import { tablet } from '../../utils/device';
|
|
|
|
import InputBlock from '../input-block'
|
|
|
|
import { Icons } from '../icons'
|
|
|
|
import Link from '../link'
|
|
|
|
import { Text } from '../text'
|
2019-10-14 06:10:22 +00:00
|
|
|
//import DropDown from '../drop-down'
|
|
|
|
|
|
|
|
import Tooltip from "../tooltip";
|
2019-09-05 07:20:12 +00:00
|
|
|
|
2019-09-10 07:38:28 +00:00
|
|
|
// eslint-disable-next-line no-unused-vars
|
2019-09-06 12:06:51 +00:00
|
|
|
const SimpleInput = ({ onValidateInput, onCopyToClipboard, ...props }) => <div {...props}></div>;
|
|
|
|
|
2019-09-09 14:34:48 +00:00
|
|
|
SimpleInput.propTypes = {
|
|
|
|
onValidateInput: PropTypes.func,
|
|
|
|
onCopyToClipboard: PropTypes.func
|
|
|
|
}
|
|
|
|
|
2019-09-06 12:06:51 +00:00
|
|
|
const StyledInput = styled(SimpleInput)`
|
2019-09-05 07:20:12 +00:00
|
|
|
display: flex;
|
|
|
|
align-items: center;
|
|
|
|
line-height: 32px;
|
|
|
|
flex-direction: row;
|
|
|
|
flex-wrap: nowrap;
|
|
|
|
|
|
|
|
@media ${tablet} {
|
|
|
|
flex-wrap: wrap;
|
|
|
|
}
|
|
|
|
`;
|
|
|
|
|
|
|
|
const PasswordProgress = styled.div`
|
|
|
|
${props => props.inputWidth ? `width: ${props.inputWidth};` : `flex: auto;`}
|
|
|
|
`;
|
|
|
|
|
|
|
|
const NewPasswordButton = styled.div`
|
|
|
|
margin-left: 16px;
|
|
|
|
margin-top: -6px;
|
|
|
|
`;
|
|
|
|
|
|
|
|
const CopyLink = styled.div`
|
|
|
|
margin-top: -6px;
|
|
|
|
margin-left: 16px;
|
|
|
|
|
|
|
|
@media ${tablet} {
|
|
|
|
width: 100%;
|
|
|
|
margin-left: 0px;
|
|
|
|
margin-top: 8px;
|
|
|
|
}
|
|
|
|
`;
|
|
|
|
|
2019-10-14 06:10:22 +00:00
|
|
|
const TooltipStyle = styled.div`
|
|
|
|
.__react_component_tooltip {
|
|
|
|
top: 84px !important;
|
|
|
|
left: 15px !important;
|
|
|
|
}
|
|
|
|
`;
|
|
|
|
|
2019-09-05 07:20:12 +00:00
|
|
|
const Progress = styled.div`
|
|
|
|
border: 3px solid ${props => (!props.isDisabled && props.progressColor) ? props.progressColor : 'transparent'};
|
|
|
|
border-radius: 2px;
|
|
|
|
margin-top: -4px;
|
|
|
|
width: ${props => props.progressWidth ? props.progressWidth + '%' : '0%'};
|
|
|
|
`;
|
|
|
|
|
|
|
|
const StyledTooltipContainer = styled(Text.Body)`
|
2019-10-14 06:10:22 +00:00
|
|
|
/*margin: 8px 16px 16px 16px;*/
|
2019-09-05 07:20:12 +00:00
|
|
|
`;
|
|
|
|
|
|
|
|
const StyledTooltipItem = styled(Text.Body)`
|
|
|
|
margin-left: 8px;
|
|
|
|
height: 24px;
|
|
|
|
color: ${props => props.valid ? '#44bb00' : '#B40404'};
|
|
|
|
`;
|
|
|
|
|
2019-09-12 10:58:17 +00:00
|
|
|
class PasswordInput extends React.Component {
|
2019-09-05 07:20:12 +00:00
|
|
|
|
|
|
|
constructor(props) {
|
|
|
|
super(props);
|
|
|
|
|
2019-09-12 10:58:17 +00:00
|
|
|
const { inputValue, inputType } = props;
|
|
|
|
|
2019-09-05 07:20:12 +00:00
|
|
|
this.state = {
|
2019-09-12 10:58:17 +00:00
|
|
|
type: inputType,
|
2019-09-05 07:20:12 +00:00
|
|
|
progressColor: 'transparent',
|
|
|
|
progressWidth: 0,
|
2019-09-12 10:58:17 +00:00
|
|
|
inputValue: inputValue,
|
2019-09-05 07:20:12 +00:00
|
|
|
displayTooltip: false,
|
|
|
|
validLength: false,
|
|
|
|
validDigits: false,
|
|
|
|
validCapital: false,
|
|
|
|
validSpecial: false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-14 06:10:22 +00:00
|
|
|
/*onFocus = () => {
|
2019-09-05 07:20:12 +00:00
|
|
|
this.setState({
|
|
|
|
displayTooltip: true
|
|
|
|
});
|
2019-10-14 06:10:22 +00:00
|
|
|
}*/
|
2019-09-05 07:20:12 +00:00
|
|
|
|
2019-10-14 06:10:22 +00:00
|
|
|
/*onBlur = () => {
|
2019-09-05 07:20:12 +00:00
|
|
|
this.setState({
|
|
|
|
displayTooltip: false
|
|
|
|
});
|
2019-10-14 06:10:22 +00:00
|
|
|
}*/
|
2019-09-05 07:20:12 +00:00
|
|
|
|
|
|
|
changeInputType = () => {
|
|
|
|
const newType = this.state.type === 'text' ? 'password' : 'text';
|
|
|
|
|
|
|
|
this.setState({
|
|
|
|
type: newType
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
testStrength = value => {
|
|
|
|
const { generatorSpecial, passwordSettings } = this.props;
|
|
|
|
const specSymbols = new RegExp('[' + generatorSpecial + ']');
|
|
|
|
|
|
|
|
let capital;
|
|
|
|
let digits;
|
|
|
|
let special;
|
|
|
|
|
|
|
|
passwordSettings.upperCase
|
|
|
|
? capital = /[A-Z]/.test(value)
|
|
|
|
: capital = true;
|
|
|
|
|
|
|
|
passwordSettings.digits
|
|
|
|
? digits = /\d/.test(value)
|
|
|
|
: digits = true;
|
|
|
|
|
|
|
|
passwordSettings.specSymbols
|
|
|
|
? special = specSymbols.test(value)
|
|
|
|
: special = true;
|
|
|
|
|
|
|
|
return {
|
|
|
|
digits: digits,
|
|
|
|
capital: capital,
|
|
|
|
special: special,
|
2019-09-06 12:06:51 +00:00
|
|
|
length: value.trim().length >= passwordSettings.minLength
|
2019-09-05 07:20:12 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
checkPassword = (value) => {
|
|
|
|
const greenColor = '#44bb00';
|
|
|
|
const redColor = '#B40404';
|
|
|
|
const passwordValidation = this.testStrength(value);
|
|
|
|
const progressScore = passwordValidation.digits
|
|
|
|
&& passwordValidation.capital
|
|
|
|
&& passwordValidation.special
|
|
|
|
&& passwordValidation.length;
|
2019-09-06 12:06:51 +00:00
|
|
|
const progressWidth = value.trim().length * 100 / this.props.passwordSettings.minLength;
|
2019-09-05 07:20:12 +00:00
|
|
|
const progressColor = progressScore
|
|
|
|
? greenColor
|
|
|
|
: (value.length === 0)
|
|
|
|
? 'transparent'
|
|
|
|
: redColor;
|
|
|
|
|
2019-09-06 12:06:51 +00:00
|
|
|
this.props.onValidateInput && this.props.onValidateInput(progressScore);
|
|
|
|
|
2019-09-05 07:20:12 +00:00
|
|
|
this.setState({
|
|
|
|
progressColor: progressColor,
|
|
|
|
progressWidth: progressWidth > 100 ? 100 : progressWidth,
|
|
|
|
inputValue: value,
|
|
|
|
validLength: passwordValidation.length,
|
|
|
|
validDigits: passwordValidation.digits,
|
|
|
|
validCapital: passwordValidation.capital,
|
|
|
|
validSpecial: passwordValidation.special
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
onChangeAction = (e) => {
|
|
|
|
this.props.onChange && this.props.onChange(e);
|
|
|
|
this.checkPassword(e.target.value);
|
|
|
|
}
|
|
|
|
|
|
|
|
onGeneratePassword = (e) => {
|
|
|
|
if (this.props.isDisabled)
|
|
|
|
return e.preventDefault();
|
|
|
|
|
|
|
|
const newPassword = this.getNewPassword();
|
|
|
|
this.checkPassword(newPassword);
|
2019-09-12 16:42:51 +00:00
|
|
|
this.props.onChange && this.props.onChange({ target: { value: newPassword } });
|
2019-09-05 07:20:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
getNewPassword = () => {
|
|
|
|
const { passwordSettings, generatorSpecial } = this.props;
|
|
|
|
|
|
|
|
const length = passwordSettings.minLength;
|
|
|
|
const string = 'abcdefghijklmnopqrstuvwxyz';
|
|
|
|
const numeric = '0123456789';
|
|
|
|
const special = generatorSpecial;
|
|
|
|
|
|
|
|
let password = '';
|
|
|
|
let character = '';
|
|
|
|
|
|
|
|
while (password.length < length) {
|
|
|
|
const a = Math.ceil(string.length * Math.random() * Math.random());
|
|
|
|
const b = Math.ceil(numeric.length * Math.random() * Math.random());
|
|
|
|
const c = Math.ceil(special.length * Math.random() * Math.random());
|
|
|
|
|
|
|
|
let hold = string.charAt(a);
|
|
|
|
|
|
|
|
if (passwordSettings.upperCase) {
|
|
|
|
hold = (password.length % 2 == 0)
|
2019-09-06 12:06:51 +00:00
|
|
|
? (hold.toUpperCase())
|
|
|
|
: (hold);
|
2019-09-05 07:20:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
character += hold;
|
2019-09-06 12:06:51 +00:00
|
|
|
|
2019-09-05 07:20:12 +00:00
|
|
|
if (passwordSettings.digits) {
|
|
|
|
character += numeric.charAt(b);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (passwordSettings.specSymbols) {
|
|
|
|
character += special.charAt(c);
|
|
|
|
}
|
2019-09-06 12:06:51 +00:00
|
|
|
|
2019-09-05 07:20:12 +00:00
|
|
|
password = character;
|
|
|
|
}
|
|
|
|
|
|
|
|
password = password
|
|
|
|
.split('')
|
|
|
|
.sort(() => 0.5 - Math.random())
|
|
|
|
.join('');
|
|
|
|
|
|
|
|
return password.substr(0, length);
|
|
|
|
}
|
|
|
|
|
|
|
|
copyToClipboard = emailInputName => {
|
2019-09-06 12:06:51 +00:00
|
|
|
const { clipEmailResource, clipPasswordResource, isDisabled, onCopyToClipboard } = this.props;
|
2019-09-05 07:20:12 +00:00
|
|
|
|
|
|
|
if (isDisabled)
|
|
|
|
return event.preventDefault();
|
|
|
|
|
|
|
|
const textField = document.createElement('textarea');
|
|
|
|
const emailValue = document.getElementsByName(emailInputName)[0].value;
|
2019-09-06 12:06:51 +00:00
|
|
|
const formattedText = clipEmailResource + emailValue + ' | ' + clipPasswordResource + this.state.inputValue;
|
2019-09-05 07:20:12 +00:00
|
|
|
|
2019-09-06 12:06:51 +00:00
|
|
|
textField.innerText = formattedText;
|
2019-09-05 07:20:12 +00:00
|
|
|
document.body.appendChild(textField);
|
|
|
|
textField.select();
|
|
|
|
document.execCommand('copy');
|
|
|
|
textField.remove();
|
2019-09-06 12:06:51 +00:00
|
|
|
|
|
|
|
onCopyToClipboard && onCopyToClipboard(formattedText);
|
2019-09-05 07:20:12 +00:00
|
|
|
}
|
|
|
|
|
2019-09-17 12:14:26 +00:00
|
|
|
shouldComponentUpdate(nextProps, nextState) {
|
2019-09-12 10:58:17 +00:00
|
|
|
return !isEqual(this.props, nextProps) || !isEqual(this.state, nextState);
|
|
|
|
}
|
|
|
|
|
2019-09-05 07:20:12 +00:00
|
|
|
render() {
|
2019-09-06 12:06:51 +00:00
|
|
|
//console.log('PasswordInput render()');
|
2019-09-05 07:20:12 +00:00
|
|
|
const {
|
|
|
|
inputName,
|
|
|
|
isDisabled,
|
|
|
|
scale,
|
|
|
|
size,
|
|
|
|
clipActionResource,
|
|
|
|
tooltipPasswordTitle,
|
|
|
|
tooltipPasswordLength,
|
|
|
|
tooltipPasswordDigits,
|
|
|
|
tooltipPasswordCapital,
|
|
|
|
tooltipPasswordSpecial,
|
|
|
|
emailInputName,
|
|
|
|
inputWidth,
|
2019-09-05 12:30:25 +00:00
|
|
|
passwordSettings,
|
|
|
|
hasError,
|
|
|
|
hasWarning,
|
|
|
|
placeholder,
|
|
|
|
tabIndex,
|
2019-09-06 12:06:51 +00:00
|
|
|
maxLength,
|
2019-09-17 12:14:26 +00:00
|
|
|
onValidateInput,
|
|
|
|
id,
|
|
|
|
autoComplete,
|
|
|
|
className
|
2019-09-05 07:20:12 +00:00
|
|
|
} = this.props;
|
|
|
|
const {
|
|
|
|
type,
|
|
|
|
progressColor,
|
|
|
|
progressWidth,
|
|
|
|
inputValue,
|
|
|
|
validLength,
|
|
|
|
validDigits,
|
|
|
|
validCapital,
|
|
|
|
validSpecial,
|
2019-10-14 06:10:22 +00:00
|
|
|
//displayTooltip
|
2019-09-05 07:20:12 +00:00
|
|
|
} = this.state;
|
|
|
|
|
|
|
|
const iconsColor = isDisabled ? '#D0D5DA' : '#A3A9AE';
|
2019-09-11 13:04:53 +00:00
|
|
|
const iconName = type === 'password' ? 'EyeIcon' : 'EyeOffIcon';
|
2019-09-05 07:20:12 +00:00
|
|
|
|
|
|
|
const tooltipContent = (
|
|
|
|
<StyledTooltipContainer forwardedAs='div' title={tooltipPasswordTitle}>
|
|
|
|
{tooltipPasswordTitle}
|
|
|
|
<StyledTooltipItem forwardedAs='div' title={tooltipPasswordLength} valid={validLength} >
|
|
|
|
{tooltipPasswordLength}
|
|
|
|
</StyledTooltipItem>
|
|
|
|
{passwordSettings.digits &&
|
|
|
|
<StyledTooltipItem forwardedAs='div' title={tooltipPasswordDigits} valid={validDigits} >
|
|
|
|
{tooltipPasswordDigits}
|
|
|
|
</StyledTooltipItem>
|
|
|
|
}
|
|
|
|
{passwordSettings.upperCase &&
|
2019-09-06 12:06:51 +00:00
|
|
|
<StyledTooltipItem forwardedAs='div' title={tooltipPasswordCapital} valid={validCapital} >
|
|
|
|
{tooltipPasswordCapital}
|
|
|
|
</StyledTooltipItem>
|
2019-09-05 07:20:12 +00:00
|
|
|
}
|
|
|
|
{passwordSettings.specSymbols &&
|
2019-09-06 12:06:51 +00:00
|
|
|
<StyledTooltipItem forwardedAs='div' title={tooltipPasswordSpecial} valid={validSpecial} >
|
|
|
|
{tooltipPasswordSpecial}
|
|
|
|
</StyledTooltipItem>
|
2019-09-05 07:20:12 +00:00
|
|
|
}
|
|
|
|
</StyledTooltipContainer>
|
|
|
|
);
|
|
|
|
|
|
|
|
return (
|
2019-09-17 12:14:26 +00:00
|
|
|
<StyledInput onValidateInput={onValidateInput} className={className}>
|
2019-10-14 06:10:22 +00:00
|
|
|
<PasswordProgress
|
|
|
|
inputWidth={inputWidth}
|
|
|
|
data-for="tooltipContent"
|
2019-10-17 10:53:11 +00:00
|
|
|
data-tip=""
|
2019-10-14 06:10:22 +00:00
|
|
|
data-event="click"
|
|
|
|
>
|
2019-09-05 07:20:12 +00:00
|
|
|
<InputBlock
|
2019-09-17 12:14:26 +00:00
|
|
|
id={id}
|
2019-09-05 07:20:12 +00:00
|
|
|
name={inputName}
|
2019-09-09 14:34:48 +00:00
|
|
|
hasError={hasError}
|
2019-09-05 07:20:12 +00:00
|
|
|
isDisabled={isDisabled}
|
2019-09-11 13:04:53 +00:00
|
|
|
iconName={iconName}
|
2019-09-05 07:20:12 +00:00
|
|
|
value={inputValue}
|
|
|
|
onIconClick={this.changeInputType}
|
|
|
|
onChange={this.onChangeAction}
|
|
|
|
scale={scale}
|
|
|
|
size={size}
|
|
|
|
type={type}
|
|
|
|
iconColor={iconsColor}
|
|
|
|
isIconFill={true}
|
2019-10-14 06:10:22 +00:00
|
|
|
//onFocus={this.onFocus}
|
|
|
|
//onBlur={this.onBlur}
|
2019-09-05 12:30:25 +00:00
|
|
|
hasWarning={hasWarning}
|
|
|
|
placeholder={placeholder}
|
|
|
|
tabIndex={tabIndex}
|
|
|
|
maxLength={maxLength}
|
2019-09-17 12:14:26 +00:00
|
|
|
autoComplete={autoComplete}
|
2019-10-14 06:10:22 +00:00
|
|
|
>
|
2019-09-05 07:20:12 +00:00
|
|
|
</InputBlock>
|
2019-10-14 06:10:22 +00:00
|
|
|
<TooltipStyle>
|
|
|
|
<Tooltip
|
|
|
|
id="tooltipContent"
|
|
|
|
getContent={() => tooltipContent}
|
|
|
|
effect="solid"
|
|
|
|
place="top"
|
|
|
|
//maxWidth={250}
|
|
|
|
/>
|
|
|
|
</TooltipStyle>
|
2019-09-05 07:20:12 +00:00
|
|
|
<Progress progressColor={progressColor} progressWidth={progressWidth} isDisabled={isDisabled} />
|
|
|
|
</PasswordProgress>
|
|
|
|
<NewPasswordButton>
|
|
|
|
<Icons.RefreshIcon
|
|
|
|
size="medium"
|
|
|
|
color={iconsColor}
|
|
|
|
isfill={true}
|
|
|
|
onClick={this.onGeneratePassword}
|
|
|
|
/>
|
|
|
|
</NewPasswordButton>
|
|
|
|
<CopyLink>
|
|
|
|
<Link
|
|
|
|
type="action"
|
|
|
|
isHovered={true}
|
|
|
|
fontSize={13}
|
|
|
|
color={iconsColor}
|
|
|
|
onClick={this.copyToClipboard.bind(this, emailInputName)}
|
|
|
|
>
|
|
|
|
{clipActionResource}
|
|
|
|
</Link>
|
|
|
|
</CopyLink>
|
|
|
|
</StyledInput>
|
|
|
|
);
|
2019-09-09 14:34:48 +00:00
|
|
|
}
|
|
|
|
}
|
2019-09-05 07:20:12 +00:00
|
|
|
|
|
|
|
PasswordInput.propTypes = {
|
|
|
|
inputType: PropTypes.oneOf(['text', 'password']),
|
|
|
|
inputName: PropTypes.string,
|
|
|
|
emailInputName: PropTypes.string.isRequired,
|
|
|
|
inputValue: PropTypes.string,
|
|
|
|
onChange: PropTypes.func,
|
2019-09-09 14:34:48 +00:00
|
|
|
inputWidth: PropTypes.string,
|
|
|
|
hasError: PropTypes.bool,
|
|
|
|
hasWarning: PropTypes.bool,
|
|
|
|
placeholder: PropTypes.string,
|
|
|
|
tabIndex: PropTypes.number,
|
|
|
|
maxLength: PropTypes.number,
|
2019-09-17 12:14:26 +00:00
|
|
|
className: PropTypes.string,
|
2019-09-05 07:20:12 +00:00
|
|
|
|
|
|
|
isDisabled: PropTypes.bool,
|
|
|
|
size: PropTypes.oneOf(['base', 'middle', 'big', 'huge']),
|
|
|
|
scale: PropTypes.bool,
|
|
|
|
|
|
|
|
clipActionResource: PropTypes.string,
|
|
|
|
clipEmailResource: PropTypes.string,
|
|
|
|
clipPasswordResource: PropTypes.string,
|
|
|
|
|
|
|
|
tooltipPasswordTitle: PropTypes.string,
|
|
|
|
tooltipPasswordLength: PropTypes.string,
|
|
|
|
tooltipPasswordDigits: PropTypes.string,
|
|
|
|
tooltipPasswordCapital: PropTypes.string,
|
|
|
|
tooltipPasswordSpecial: PropTypes.string,
|
|
|
|
|
|
|
|
generatorSpecial: PropTypes.string,
|
2019-09-06 12:06:51 +00:00
|
|
|
passwordSettings: PropTypes.object.isRequired,
|
|
|
|
|
|
|
|
onValidateInput: PropTypes.func,
|
|
|
|
onCopyToClipboard: PropTypes.func
|
2019-09-05 07:20:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
PasswordInput.defaultProps = {
|
|
|
|
inputType: 'password',
|
|
|
|
inputName: 'passwordInput',
|
2019-09-17 12:14:26 +00:00
|
|
|
autoComplete: 'new-password',
|
2019-09-05 07:20:12 +00:00
|
|
|
|
2019-09-06 12:06:51 +00:00
|
|
|
isDisabled: false,
|
2019-09-05 07:20:12 +00:00
|
|
|
size: 'base',
|
|
|
|
scale: true,
|
|
|
|
|
2019-09-06 12:06:51 +00:00
|
|
|
clipEmailResource: 'E-mail ',
|
|
|
|
clipPasswordResource: 'Password ',
|
2019-09-05 07:20:12 +00:00
|
|
|
|
2019-09-17 12:14:26 +00:00
|
|
|
generatorSpecial: '!@#$%^&*',
|
|
|
|
className: ''
|
2019-09-05 07:20:12 +00:00
|
|
|
}
|
|
|
|
|
2019-10-14 06:10:22 +00:00
|
|
|
export default PasswordInput;
|
|
|
|
/*
|
|
|
|
{displayTooltip &&
|
|
|
|
<DropDown directionY='top' manualY='150%' isOpen={true}>
|
|
|
|
{tooltipContent}
|
|
|
|
</DropDown>
|
|
|
|
}
|
|
|
|
*/
|