TogglablePassword.tsx
1 // Either self contained, or controlled component for having a password field 2 // with a toggle to turn it into a visible text field. 3 // Pass `isVisible` and `handleToggleVisibility` to control the visibility 4 // yourself, otherwise all visibiility changes are managed in internal state. 5 import React from 'react'; 6 7 import { Input, TextArea } from 'components/ui'; 8 import './TogglablePassword.scss'; 9 10 interface Props { 11 // Shared props 12 className?: string; 13 value: string; 14 placeholder?: string; 15 name?: string; 16 disabled?: boolean; 17 ariaLabel?: string; 18 toggleAriaLabel?: string; 19 isValid?: boolean; 20 isVisible?: boolean; 21 readOnly?: boolean; 22 23 // Textarea-only props 24 isTextareaWhenVisible?: boolean; 25 rows?: number; 26 onEnter?(): void; 27 28 // Shared callbacks 29 onChange?(ev: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>): void; 30 onFocus?(ev: React.FocusEvent<HTMLInputElement | HTMLTextAreaElement>): void; 31 onBlur?(ev: React.FocusEvent<HTMLInputElement | HTMLTextAreaElement>): void; 32 handleToggleVisibility?(): void; 33 } 34 35 interface State { 36 isVisible: boolean; 37 } 38 39 export default class TogglablePassword extends React.PureComponent<Props, State> { 40 public state: State = { 41 isVisible: !!this.props.isVisible 42 }; 43 44 public UNSAFE_componentWillReceiveProps(nextProps: Props) { 45 if (this.props.isVisible !== nextProps.isVisible) { 46 this.setState({ isVisible: !!nextProps.isVisible }); 47 } 48 } 49 50 public render() { 51 const { 52 className, 53 value, 54 placeholder, 55 name, 56 disabled, 57 ariaLabel, 58 toggleAriaLabel, 59 isTextareaWhenVisible, 60 isValid, 61 onChange, 62 onFocus, 63 onBlur, 64 handleToggleVisibility, 65 readOnly 66 } = this.props; 67 const { isVisible } = this.state; 68 69 return ( 70 <div className={`TogglablePassword input-group input-group-inline`}> 71 {isTextareaWhenVisible && isVisible ? ( 72 <TextArea 73 isValid={!!isValid} 74 className={className} 75 value={value} 76 name={name} 77 disabled={disabled} 78 onChange={onChange} 79 onKeyDown={this.handleTextareaKeyDown} 80 onFocus={onFocus} 81 onBlur={onBlur} 82 placeholder={placeholder} 83 rows={this.props.rows || 3} 84 aria-label={ariaLabel} 85 readOnly={readOnly} 86 /> 87 ) : ( 88 <Input 89 isValid={!!isValid} 90 value={value} 91 name={name} 92 disabled={disabled} 93 type={isVisible ? 'text' : 'password'} 94 className={`${className} border-rad-right-0`} 95 placeholder={placeholder} 96 onChange={onChange} 97 onFocus={onFocus} 98 onBlur={onBlur} 99 aria-label={ariaLabel} 100 readOnly={readOnly} 101 /> 102 )} 103 <span 104 onClick={handleToggleVisibility || this.toggleVisibility} 105 aria-label={toggleAriaLabel} 106 role="button" 107 className="TogglablePassword-toggle input-group-addon" 108 > 109 <i className={`fa fa-${isVisible ? 'eye-slash' : 'eye'}`} /> 110 </span> 111 </div> 112 ); 113 } 114 115 private toggleVisibility = () => { 116 this.setState({ isVisible: !this.state.isVisible }); 117 }; 118 119 private handleTextareaKeyDown = (ev: React.KeyboardEvent<HTMLTextAreaElement>) => { 120 if (this.props.onEnter && ev.keyCode === 13) { 121 ev.preventDefault(); 122 this.props.onEnter(); 123 } 124 }; 125 }