chore (litmus-portal): Refactor Settings section to use InputField (#2000)

Refactored all the components inside of the Settings section to use the unified and customisable InputField component. This will ensures consistency in styling as well as input handling and validation methods.

Signed-off-by: Saswata Mukherjee <saswataminsta@yahoo.com>

Co-authored-by: Sayan Mondal <sayan.mondal@mayadata.io>
This commit is contained in:
Saswata Mukherjee 2020-09-08 18:39:33 +05:30 committed by GitHub
parent f29c20e2cd
commit 85b495541a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 178 additions and 451 deletions

View File

@ -1,24 +1,17 @@
import { Button, Divider, Typography } from '@material-ui/core';
import React, { useRef } from 'react';
import InputField from '../../../../../containers/layouts/InputField';
import Unimodal from '../../../../../containers/layouts/Unimodal';
import {
FormControl,
IconButton,
Input,
InputAdornment,
InputLabel,
Button,
Typography,
Divider,
} from '@material-ui/core';
import Visibility from '@material-ui/icons/Visibility';
import VisibilityOff from '@material-ui/icons/VisibilityOff';
import React from 'react';
validateConfirmPassword,
validateStartEmptySpacing,
} from '../../../../../utils/validate';
import PersonalDetails from '../PersonalDetails';
import useStyles from './styles';
import Unimodal from '../../../../../containers/layouts/Unimodal';
// used for password field
interface Password {
password: string;
err: boolean;
showPassword: boolean;
}
@ -28,101 +21,62 @@ const AccountSettings: React.FC = () => {
// used for modal
const [open, setOpen] = React.useState(false);
const isSuccess = useRef<boolean>(false);
const handleClose = () => {
setOpen(false);
};
// used for password validation
const regularExpression = new RegExp(
'^(?=.*[a-zA-Z])(?=.*[0-9])(?=.*[!@#$%^&*])(?=.{8,})'
);
// states for the three password fields
const [currPassword, setCurrPassword] = React.useState<Password>({
password: '',
showPassword: false,
err: false,
});
const [newPassword, setNewPassword] = React.useState<Password>({
password: '',
showPassword: false,
err: false,
});
const [confNewPassword, setConfNewPassword] = React.useState<Password>({
password: '',
showPassword: false,
err: false,
});
// handleNewPassword handles password validation for second password field
// handleCurrPassword handles password for first password field
const handleCurrPassword = (prop: keyof Password) => (
event: React.ChangeEvent<HTMLInputElement>
) => {
setCurrPassword({
...currPassword,
[prop]: event.target.value,
});
};
// handleNewPassword handles password for second password field
const handleNewPassword = (prop: keyof Password) => (
event: React.ChangeEvent<HTMLInputElement>
) => {
if (
event.target.value !== currPassword.password &&
regularExpression.test(event.target.value) &&
(confNewPassword.password.length === 0 ||
event.target.value === confNewPassword.password)
) {
setNewPassword({
...newPassword,
err: false,
[prop]: event.target.value,
});
setConfNewPassword({ ...confNewPassword, err: false });
} else {
setNewPassword({ ...newPassword, err: true, [prop]: event.target.value });
setConfNewPassword({ ...confNewPassword });
}
setNewPassword({
...newPassword,
[prop]: event.target.value,
});
};
// handleConfPassword handles password validation for third password field
// handleConfPassword handles password for third password field
const handleConfPassword = (prop: keyof Password) => (
event: React.ChangeEvent<HTMLInputElement>
) => {
if (
event.target.value === newPassword.password &&
regularExpression.test(event.target.value)
) {
setConfNewPassword({
...confNewPassword,
err: false,
[prop]: event.target.value,
});
setNewPassword({ ...newPassword, err: false });
} else {
setConfNewPassword({
...confNewPassword,
err: true,
[prop]: event.target.value,
});
setNewPassword({ ...newPassword });
}
};
// implements the logic for visibility of password
const handleClickShowPassword1 = () => {
setCurrPassword({
...currPassword,
showPassword: !currPassword.showPassword,
});
};
const handleClickShowPassword2 = () => {
setNewPassword({ ...newPassword, showPassword: !newPassword.showPassword });
};
const handleClickShowPassword3 = () => {
setConfNewPassword({
...confNewPassword,
showPassword: !confNewPassword.showPassword,
[prop]: event.target.value,
});
};
const handleMouseDownPassword = (
event: React.MouseEvent<HTMLButtonElement>
) => {
event.preventDefault();
};
if (
confNewPassword.password.length > 0 &&
newPassword.password === confNewPassword.password
)
isSuccess.current = true;
else isSuccess.current = false;
return (
<div className={classes.container}>
@ -139,106 +93,60 @@ const AccountSettings: React.FC = () => {
<div className={classes.outerPass}>
<form className={classes.innerPass}>
{/* Current Password */}
<FormControl>
<Input
className={classes.pass}
defaultValue={currPassword.password}
id="outlined-adornment-password"
type={currPassword.showPassword ? 'text' : 'password'}
disableUnderline
endAdornment={
<InputAdornment position="end">
<IconButton
aria-label="toggle password visibility"
onClick={handleClickShowPassword1}
edge="end"
>
{currPassword.showPassword ? (
<Visibility />
) : (
<VisibilityOff />
)}
</IconButton>
</InputAdornment>
}
/>
<InputLabel htmlFor="outlined-adornment-password">
Current Password
</InputLabel>
</FormControl>
<InputField
required
value={currPassword.password}
handleChange={handleCurrPassword('password')}
type="password"
label="Current Password"
validationError={false}
/>
{/* New Password */}
<FormControl>
<Input
data-cy="changePassword"
className={`${classes.pass} ${
newPassword.err ? classes.error : classes.success
}`}
id="outlined-adornment-password"
type={newPassword.showPassword ? 'text' : 'password'}
onChange={handleNewPassword('password')}
disableUnderline
endAdornment={
<InputAdornment position="end">
<IconButton
data-cy="conVisibilty"
aria-label="toggle password visibility"
onClick={handleClickShowPassword2}
onMouseDown={handleMouseDownPassword}
edge="end"
>
{newPassword.showPassword ? (
<Visibility data-cy="visIcon" />
) : (
<VisibilityOff data-cy="invisIcon" />
)}
</IconButton>
</InputAdornment>
}
/>
<InputLabel htmlFor="outlined-adornment-password">
New Password
</InputLabel>
</FormControl>
<InputField
required
type="password"
handleChange={handleNewPassword('password')}
success={isSuccess.current}
helperText={
validateStartEmptySpacing(newPassword.password)
? 'Should not start with empty space'
: ''
}
label="New Password"
validationError={validateStartEmptySpacing(
newPassword.password
)}
value={newPassword.password}
/>
{/* Confirm new password */}
<FormControl>
<Input
data-cy="confirmPassword"
className={`${classes.pass} ${
confNewPassword.err ? classes.error : classes.success
}`}
id="outlined-adornment-password"
type={confNewPassword.showPassword ? 'text' : 'password'}
onChange={handleConfPassword('password')}
disableUnderline
endAdornment={
<InputAdornment position="end">
<IconButton
data-cy="conVisibilty"
aria-label="toggle password visibility"
onClick={handleClickShowPassword3}
onMouseDown={handleMouseDownPassword}
edge="end"
>
{confNewPassword.showPassword ? (
<Visibility data-cy="visIcon" />
) : (
<VisibilityOff data-cy="invisIcon" />
)}
</IconButton>
</InputAdornment>
}
/>
<InputLabel htmlFor="outlined-adornment-password">
Confirm new password
</InputLabel>
</FormControl>
<InputField
helperText={
validateConfirmPassword(
newPassword.password,
confNewPassword.password
)
? 'Password is not same'
: ''
}
required
type="password"
handleChange={handleConfPassword('password')}
success={isSuccess.current}
label="Confirm Password"
validationError={validateConfirmPassword(
newPassword.password,
confNewPassword.password
)}
value={confNewPassword.password}
/>
<Button
data-cy="button"
variant="contained"
className={classes.button}
onClick={() => {
if (
!(newPassword.err && confNewPassword.err) &&
newPassword.password.length > 0 &&
confNewPassword.password.length > 0
) {

View File

@ -44,14 +44,6 @@ const useStyles = makeStyles((theme: Theme) => ({
display: 'flex',
flexDirection: 'column',
},
pass: {
width: '24.43rem',
height: '4.8125rem',
marginBottom: theme.spacing(2.5),
padding: theme.spacing(1),
border: '1px solid rgba(0, 0, 0, 0.2)',
borderRadius: 3,
},
col2: {
display: 'flex',
flexDirection: 'column',
@ -74,14 +66,6 @@ const useStyles = makeStyles((theme: Theme) => ({
marginBottom: theme.spacing(2.5),
fontSize: '1rem',
},
success: {
border: '0.0625rem solid',
borderColor: theme.palette.secondary.dark,
},
error: {
border: '0.0625rem solid',
borderColor: theme.palette.error.main,
},
// Password Modal content styles
body: {

View File

@ -1,7 +1,12 @@
import { Avatar, TextField, Typography } from '@material-ui/core';
import { Avatar, Typography } from '@material-ui/core';
import React from 'react';
import InputField from '../../../../../../containers/layouts/InputField';
import userAvatar from '../../../../../../utils/user';
import useStyles from './styles';
import {
validateEmail,
validateStartEmptySpacing,
} from '../../../../../../utils/validate';
interface PersonalDetailsProps {
handleNameChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
@ -53,45 +58,39 @@ const UserDetails: React.FC<PersonalDetailsProps> = ({
</div>
{/* Fields for details including Full name, email, username */}
<div className={classes.details1}>
<TextField
<InputField
required
helperText={
validateStartEmptySpacing(nameValue)
? 'Should not start with an empty space'
: ''
}
value={nameValue}
disabled={nameIsDisabled}
onChange={handleNameChange}
className={classes.user}
id="filled-user-input"
handleChange={handleNameChange}
validationError={validateStartEmptySpacing(nameValue)}
label="Full Name"
InputProps={{ disableUnderline: true }}
data-cy="fullName"
/>
<TextField
<InputField
required
helperText={
validateEmail(emailValue) ? 'Should be a valid email' : ''
}
type="email"
value={emailValue}
disabled={emailIsDisabled}
onChange={handleEmailChange}
className={classes.user}
id="filled-email-input"
handleChange={handleEmailChange}
validationError={validateEmail(emailValue)}
label="Email Address"
name="email"
InputProps={{
disableUnderline: true,
}}
data-cy="inputEmail"
/>
{/* Username is not editable normal user */}
<TextField
<InputField
value={userValue}
onChange={handleUserChange}
className={classes.user}
id="filled-username-input"
handleChange={handleUserChange}
label="Username"
disabled={usernameIsDisabled}
InputProps={{
disableUnderline: true,
}}
data-cy="username"
validationError={false}
/>
</div>
</div>

View File

@ -48,16 +48,6 @@ const useStyles = makeStyles((theme: Theme) => ({
marginRight: theme.spacing(2.5),
},
user: {
border: '1px solid rgba(0, 0, 0, 0.2)',
borderRadius: '0.1875rem',
width: '24.43rem',
height: '4.8125rem',
marginLeft: theme.spacing(2.5),
marginRight: theme.spacing(2.5),
paddingLeft: theme.spacing(3.75),
marginBottom: theme.spacing(2.5),
},
// Style for ProfileDropdownSection and ProfileDropdownItems.
avatarBackground: {
backgroundColor: theme.palette.secondary.main,

View File

@ -1,23 +1,16 @@
import {
Divider,
FormControl,
IconButton,
Input,
InputAdornment,
InputLabel,
TextField,
Typography,
} from '@material-ui/core';
import Visibility from '@material-ui/icons/Visibility';
import VisibilityOff from '@material-ui/icons/VisibilityOff';
import { Divider, IconButton, Typography } from '@material-ui/core';
import React from 'react';
import NewUserModal from './NewUserModal';
import InputField from '../../../../../containers/layouts/InputField';
import useStyles from './styles';
import UserDetails from './UserDetails';
import {
validateStartEmptySpacing,
validateLength,
} from '../../../../../utils/validate';
interface Password {
password: string;
err: boolean;
showPassword: boolean;
}
@ -36,51 +29,22 @@ interface CreateUserProps {
const CreateUser: React.FC<CreateUserProps> = ({ handleDiv }) => {
const classes = useStyles();
// for password validation
const regularExpression = new RegExp(
'^(?=.*[a-zA-Z])(?=.*[0-9])(?=.*[!@#$%^&*])(?=.{8,})'
);
// for conditional rendering of reset password div
const [createPAssword, setCreatePassword] = React.useState<Password>({
password: '',
showPassword: false,
err: false,
});
// handles password field
const handleCreatePassword = (prop: keyof Password) => (
event: React.ChangeEvent<HTMLInputElement>
) => {
if (regularExpression.test(event.target.value)) {
setCreatePassword({
...createPAssword,
err: false,
[prop]: event.target.value,
});
} else {
setCreatePassword({
...createPAssword,
err: true,
[prop]: event.target.value,
});
}
};
const handleClickShowPassword = () => {
setCreatePassword({
...createPAssword,
showPassword: !createPAssword.showPassword,
[prop]: event.target.value,
});
};
const handleMouseDownPassword = (
event: React.MouseEvent<HTMLButtonElement>
) => {
event.preventDefault();
};
// for personal details fields
const [personalData, setPersonalData] = React.useState<personalData>({
email: '',
@ -88,6 +52,13 @@ const CreateUser: React.FC<CreateUserProps> = ({ handleDiv }) => {
fullName: '',
});
const handleUsername = (event: React.ChangeEvent<HTMLInputElement>) => {
setPersonalData({
...personalData,
userName: event.target.value,
});
};
return (
<div>
<div className={classes.headDiv}>
@ -149,53 +120,35 @@ const CreateUser: React.FC<CreateUserProps> = ({ handleDiv }) => {
<div>
<form>
<div className={classes.details1}>
<TextField
className={classes.userDetail}
id="filled-username-input"
<InputField
helperText={
validateStartEmptySpacing(personalData.userName)
? 'Should not start with an empty space'
: ''
}
label="Username"
defaultValue="RichardHill"
value={personalData.userName}
handleChange={handleUsername}
validationError={validateStartEmptySpacing(
personalData.userName
)}
disabled
InputProps={{
disableUnderline: true,
}}
data-cy="username"
/>
<FormControl>
<Input
required
data-cy="changePassword"
className={`${classes.pass} ${
createPAssword.err ? classes.error : classes.success
}`}
id="outlined-adornment-password"
type={
createPAssword.showPassword ? 'text' : 'password'
}
onChange={handleCreatePassword('password')}
disableUnderline
endAdornment={
<InputAdornment position="end">
<IconButton
data-cy="conVisibilty"
aria-label="toggle password visibility"
onClick={handleClickShowPassword}
onMouseDown={handleMouseDownPassword}
edge="end"
>
{createPAssword.showPassword ? (
<Visibility data-cy="visIcon" />
) : (
<VisibilityOff data-cy="invisIcon" />
)}
</IconButton>
</InputAdornment>
}
/>
<InputLabel htmlFor="outlined-adornment-password">
New Password
</InputLabel>
</FormControl>
<InputField
required
type="password"
helperText={
validateLength(createPAssword.password)
? 'Password is too short'
: ''
}
handleChange={handleCreatePassword('password')}
value={createPAssword.password}
validationError={validateLength(
createPAssword.password
)}
label="New Password"
/>
</div>
</form>
</div>
@ -206,7 +159,6 @@ const CreateUser: React.FC<CreateUserProps> = ({ handleDiv }) => {
<div className={classes.buttonGroup}>
<NewUserModal
showModal={
!createPAssword.err &&
personalData.userName.length > 0 &&
createPAssword.password.length > 0
}

View File

@ -56,26 +56,6 @@ const useStyles = makeStyles((theme: Theme) => ({
alignContent: 'flex-start',
flexWrap: 'wrap',
},
// for username field
userDetail: {
border: '1px solid rgba(0, 0, 0, 0.2)',
borderRadius: 3,
width: '24.43rem',
height: '4.8125rem',
marginRight: theme.spacing(2.5),
paddingLeft: theme.spacing(3.75),
marginBottom: theme.spacing(2.5),
},
// for password
pass: {
width: '24.43rem',
height: '4.8125rem',
marginBottom: theme.spacing(2.5),
padding: theme.spacing(1),
border: '1px solid rgba(0, 0, 0, 0.2)',
borderRadius: 3,
},
divider: {
marginTop: theme.spacing(3.75),
@ -89,17 +69,6 @@ const useStyles = makeStyles((theme: Theme) => ({
color: theme.palette.text.disabled,
},
// button success and button error
success: {
border: '0.0625rem solid',
borderColor: theme.palette.secondary.dark,
},
error: {
border: '0.0625rem solid',
borderColor: theme.palette.error.main,
},
createRandomButton: {
background: theme.palette.secondary.dark,
color: theme.palette.secondary.contrastText,

View File

@ -1,23 +1,14 @@
import {
Divider,
FormControl,
IconButton,
Input,
InputAdornment,
InputLabel,
Typography,
} from '@material-ui/core';
import Visibility from '@material-ui/icons/Visibility';
import VisibilityOff from '@material-ui/icons/VisibilityOff';
import { Divider, IconButton, Typography } from '@material-ui/core';
import React from 'react';
import DelUser from './DelUser';
import ResetModal from './ResetModal';
import UserDetails from '../CreateUser/UserDetails';
import useStyles from './styles';
import InputField from '../../../../../containers/layouts/InputField';
import { validateLength } from '../../../../../utils/validate';
interface Password {
password: string;
err: boolean;
showPassword: boolean;
}
@ -38,51 +29,23 @@ const EditUser: React.FC<EditUserProps> = ({
}) => {
const classes = useStyles();
// for password validation
const regularExpression = new RegExp(
'^(?=.*[a-zA-Z])(?=.*[0-9])(?=.*[!@#$%^&*])(?=.{8,})'
);
// for conditional rendering of reset password div
const [createPAssword, setCreatePassword] = React.useState<Password>({
password: '',
showPassword: false,
err: false,
});
// handles password field
const handleCreatePassword = (prop: keyof Password) => (
event: React.ChangeEvent<HTMLInputElement>
) => {
if (regularExpression.test(event.target.value)) {
setCreatePassword({
...createPAssword,
err: false,
[prop]: event.target.value,
});
} else {
setCreatePassword({
...createPAssword,
err: true,
[prop]: event.target.value,
});
}
};
const handleClickShowPassword = () => {
setCreatePassword({
...createPAssword,
showPassword: !createPAssword.showPassword,
[prop]: event.target.value,
});
};
const handleMouseDownPassword = (
event: React.MouseEvent<HTMLButtonElement>
) => {
event.preventDefault();
};
return (
<div>
<div className={classes.headDiv}>
@ -122,41 +85,21 @@ const EditUser: React.FC<EditUserProps> = ({
<div>
<form>
<div className={classes.details1}>
<FormControl>
<Input
required
data-cy="changePassword"
className={`${classes.pass} ${
createPAssword.err ? classes.error : classes.success
}`}
id="outlined-adornment-password"
type={
createPAssword.showPassword ? 'text' : 'password'
}
onChange={handleCreatePassword('password')}
disableUnderline
endAdornment={
<InputAdornment position="end">
<IconButton
data-cy="conVisibilty"
aria-label="toggle password visibility"
onClick={handleClickShowPassword}
onMouseDown={handleMouseDownPassword}
edge="end"
>
{createPAssword.showPassword ? (
<Visibility data-cy="visIcon" />
) : (
<VisibilityOff data-cy="invisIcon" />
)}
</IconButton>
</InputAdornment>
}
/>
<InputLabel htmlFor="outlined-adornment-password">
New Password
</InputLabel>
</FormControl>
<InputField
required
helperText={
validateLength(createPAssword.password)
? 'Password is too short'
: ''
}
handleChange={handleCreatePassword('password')}
type="password"
validationError={validateLength(
createPAssword.password
)}
label="New Password"
value={createPAssword.password}
/>
</div>
<Divider className={classes.divider} />
<DelUser handleModal={handleDiv} tableDelete={false} />

View File

@ -67,16 +67,6 @@ const useStyles = makeStyles((theme: Theme) => ({
marginBottom: theme.spacing(2.5),
},
// for password
pass: {
width: '24.43rem',
height: '4.8125rem',
marginBottom: theme.spacing(2.5),
padding: theme.spacing(1),
border: '1px solid rgba(0, 0, 0, 0.2)',
borderRadius: 3,
},
divider: {
marginTop: theme.spacing(3.75),
maxWidth: '58.75rem',
@ -89,17 +79,6 @@ const useStyles = makeStyles((theme: Theme) => ({
color: theme.palette.text.disabled,
},
// button success and button error
success: {
border: '0.0625rem solid',
borderColor: theme.palette.secondary.dark,
},
error: {
border: '0.0625rem solid',
borderColor: theme.palette.error.main,
},
createRandomButton: {
background: theme.palette.secondary.dark,
color: theme.palette.secondary.contrastText,

View File

@ -4,6 +4,7 @@ import {
createStyles,
FormControl,
IconButton,
InputBase,
InputAdornment,
Menu,
MenuItem,
@ -15,7 +16,6 @@ import {
TableContainer,
TableHead,
TableRow,
TextField,
Theme,
Toolbar,
Typography,
@ -148,8 +148,8 @@ const UserManagement: React.FC = () => {
<div>
<Toolbar className={classes.toolbar}>
{/* Search user */}
<TextField
id="input-with-icon-textfield"
<InputBase
id="input-with-icon-adornment"
placeholder="Search..."
onChange={(e) => {
setRows(
@ -158,16 +158,11 @@ const UserManagement: React.FC = () => {
)
);
}}
InputProps={{
style: {
maxWidth: '15.75rem',
},
startAdornment: (
<InputAdornment position="start">
<SearchIcon />
</InputAdornment>
),
}}
startAdornment={
<InputAdornment position="start">
<SearchIcon />
</InputAdornment>
}
/>
{/* filter menu */}
<div className={classes.filter}>

View File

@ -8,7 +8,7 @@ import ButtonFilled from '../Button/ButtonFilled';
import config from '../../config';
import { CREATE_USER } from '../../graphql';
import { RootState } from '../../redux/reducers';
import InputField from '../InputField';
import InputField from '../../containers/layouts/InputField';
import ModalPage from './Modalpage';
import useStyles from './styles';
import {

View File

@ -13,16 +13,18 @@ interface InputFieldProps {
helperText?: string;
validationError: boolean;
success?: boolean;
disabled?: boolean;
value: string;
required: boolean;
required?: boolean;
iconType?: string | undefined;
handleChange: (event: React.ChangeEvent<{ value: string }>) => void;
handleChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
}
const InputField: React.FC<InputFieldProps> = ({
type,
label,
value,
disabled,
helperText,
validationError,
success,
@ -310,6 +312,7 @@ const InputField: React.FC<InputFieldProps> = ({
<TextField
className={LitmusTextFieldStylesExternal.inputArea}
error={validationError}
disabled={disabled}
label={label}
helperText={helperText}
value={value}

View File

@ -2,7 +2,7 @@ import { Button, Typography } from '@material-ui/core';
import React, { useState } from 'react';
import { Link } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import InputField from '../../components/InputField';
import InputField from '../../containers/layouts/InputField';
import config from '../../config';
import useActions from '../../redux/actions';
import * as UserActions from '../../redux/actions/user';

View File

@ -44,3 +44,8 @@ export const validateOnlyAlphabet = (value: string) => {
if (value.match(onlyAplhaValid)) return false;
return true;
};
export const validateLength = (value: string) => {
if (value.length > 0) return false;
return true;
};