Skip to content

Commit

Permalink
feat(app-general): improve signin and signup ui
Browse files Browse the repository at this point in the history
  • Loading branch information
albertodigioacchino committed Feb 5, 2021
1 parent 348bae6 commit fc9140b
Show file tree
Hide file tree
Showing 14 changed files with 204 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export type Variants = 'default' | 'clear';

export const StyledButton = styled('button')<{ variant: Variants }>`
height: 40px;
min-width: 200px;
border-radius: 4px;
padding: 2px 40px;
cursor: pointer;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export const StyledInput = styled.input<{ variant: InputVariants; withIcon?: boo
padding: 5px 10px;
box-sizing: border-box;
border-radius: 4px;
display: flex;
&:focus {
outline: none;
Expand Down
12 changes: 7 additions & 5 deletions packages/game-app/src/_shared/components/Link/Link.styled.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import styled from 'styled-components';

export const LinkButton = styled.button`
export const LinkButton = styled.button<{ color?: 'blue' | 'gray'; fontSize?: string }>`
background-color: transparent;
border: 0;
text-decoration: underline;
font-size: 15px;
color: #9f998f;
font-size: ${(props: { color?: 'blue' | 'gray'; fontSize?: string }) => props.fontSize ?? '16px'};
margin-top: ${(props: { color?: 'blue' | 'gray' }) => (props.color === 'gray' ? '-4px' : '-1.5px')};
font-weight: ${(props: { color?: 'blue' | 'gray' }) => (props.color === 'gray' ? 'none' : '600')};
font-family: 'Montserrat';
color: ${(props: { color?: 'blue' | 'gray' }) => (props.color === 'gray' ? '#9F998F' : '#148AB3')};
cursor: pointer;
text-decoration: ${(props: { color?: 'blue' | 'gray' }) => (props.color === 'gray' ? 'underline' : 'none')};
:focus {
outline: none;
}
Expand Down
6 changes: 4 additions & 2 deletions packages/game-app/src/_shared/components/Link/Link.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ import { LinkButton } from './Link.styled';
type Props = {
onClick: () => void;
id?: string;
color?: 'blue' | 'gray';
fontSize?: string;
};

const Link: React.FC<Props> = ({ onClick, children, id }) => {
const Link: React.FC<Props> = ({ onClick, children, id, color, fontSize }) => {
return (
<LinkButton id={id} data-cy={id} type="button" onClick={onClick}>
<LinkButton id={id} data-cy={id} type="button" onClick={onClick} color={color} fontSize={fontSize}>
{children}
</LinkButton>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,13 @@ import Input from '../Input';

export const InputContainer = styled(Box)<React.ComponentProps<typeof Box>>`
flex-direction: column;
${Input} {
margin-top: 5px;
}
`;

export const LabelContainer = styled(Box)<React.ComponentProps<typeof Box>>`
flex-direction: row;
justify-content: space-between;
display: flex;
`;
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,49 @@ import React, { useState } from 'react';
import IconButton from '../IconButton';
import ErrorMessage from '../ErrorMessage';
import Typography from '../Typography';
import { InputContainer } from './PasswordInput.styled';
import { InputContainer, LabelContainer } from './PasswordInput.styled';
import Input from '../Input';
import { ReactComponent as EyeIcon } from '../../../assets/icons/eye.svg';
import { Link } from '@pipeline/components';

type Props = {
name: string;
label?: string;
placeholder?: string;
value: string;
onChange?: React.ChangeEventHandler<HTMLInputElement>;
errorMessage?: string | null;
onForgotPassword?: () => void;
forgotPasswordLabel?: string;
};

const PasswordInput = React.forwardRef<HTMLInputElement, Props>(
({ name, value, errorMessage, label, onChange }, ref) => {
({ name, value, errorMessage, label, placeholder, onChange, forgotPasswordLabel, onForgotPassword }, ref) => {
const [type, setType] = useState<'text' | 'password'>('password');

const toggleType = () => setType(type => (type === 'text' ? 'password' : 'text'));

return (
<InputContainer>
<Typography mb={1} as="label" variant="label" htmlFor={name}>
{label}
</Typography>
<LabelContainer>
<Typography mb={1} as="label" variant="label" htmlFor={name}>
{label}
</Typography>
{forgotPasswordLabel && onForgotPassword && (
<Link color="gray" fontSize="14px" onClick={onForgotPassword}>
{forgotPasswordLabel}
</Link>
)}
</LabelContainer>
<Input
ref={ref}
variant="default"
iconRight={
<IconButton variant="clearSmall" onClick={toggleType}>
<i className="gg-eye" />
<EyeIcon className="gg-eye" />
</IconButton>
}
placeholder={placeholder}
type={type}
value={value}
name={name}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import Input from '../Input';
type Props = {
name: string;
label?: string;
placeholder?: string;
value: string;
onChange?: React.ChangeEventHandler<HTMLInputElement>;
errorMessage?: string | null;
Expand All @@ -15,7 +16,7 @@ type Props = {
};

const TextInput = React.forwardRef<HTMLInputElement, Props>(
({ name, value, errorMessage, label, onChange, type = 'text', disabled }, ref) => {
({ name, value, errorMessage, label, placeholder, onChange, type = 'text', disabled }, ref) => {
return (
<InputContainer>
{label ? (
Expand All @@ -27,6 +28,7 @@ const TextInput = React.forwardRef<HTMLInputElement, Props>(
ref={ref}
variant="default"
disabled={disabled}
placeholder={placeholder}
type={type}
value={value}
name={name}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,21 @@ type Props = {
* Label to show over the input
*/
label?: string;
/**
* Placeholder to show inside the input
*/
placeholder?: string;
type?: string;
CustomInput?: React.ComponentType<React.ComponentProps<typeof TextInput>>;
disabled?: boolean;
others?: object;
};

/**
* Input directly connected to the parent form that includes error message
* visualization. Its value can be found under the {name} key in the form
*/
const FormTextField: React.FC<Props> = ({ name, label, type, CustomInput, disabled }) => {
const FormTextField: React.FC<Props> = ({ name, label, placeholder, type, CustomInput, disabled, others }) => {
const data = useFormContext();

const error = data.errors[name];
Expand All @@ -43,15 +48,17 @@ const FormTextField: React.FC<Props> = ({ name, label, type, CustomInput, disabl
<Input
name={props.name}
label={label}
placeholder={placeholder}
value={props.value}
onChange={props.onChange}
errorMessage={translatedError}
type={type}
disabled={disabled}
{...others}
/>
);
},
[CustomInput, label, translatedError, type, disabled],
[CustomInput, label, placeholder, translatedError, type, disabled, others],
);

return (
Expand Down
1 change: 1 addition & 0 deletions packages/game-app/src/_shared/routing/routingPath.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export enum RoutingPath {
Dashboard = '/dashboard',
EmailVerificationRequired = '/email-verification-required',
VerifyEmail = '/verify-email',
ForgotPassword = '/forgot-password',
Game = '/game',
CreateGame = '/create-game',
}
16 changes: 16 additions & 0 deletions packages/game-app/src/_shared/routing/useNavigateOutsideTo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { useCallback } from 'react';
import { useHistory } from 'react-router-dom';
import { RoutingPath } from './routingPath';

/**
*
* Returns a memoized callback to imperatively navigate outside the app
*
* @param url the url to go
* @param openNewTab flag to open a new tab
*/
export default function useNavigateOutsideTo(url: string, openNewTab?: boolean) {
return useCallback(() => {
window.open(url, openNewTab ? '_blank' : undefined);
}, [url, openNewTab]);
}
48 changes: 40 additions & 8 deletions packages/game-app/src/assets/i18n/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,21 @@ const translations = {
notYetAccount: "Haven't played yet?",
goToSignup: 'Create account',
form: {
emailLabel: 'Email',
passwordLabel: 'Password',
email: {
label: 'Email',
placeholder: 'james.smith@eficode.com',
},
password: {
label: 'Password',
placeholder: 'Test1234',
forgot: 'Forgot?',
},
buttonText: 'Sign in',
},
privacy: {
text: "By signing in I agree to Eficode's",
link: 'Privacy Policy',
},
errors: {
'auth/invalid-email': 'Email not valid',
'auth/user-disabled': 'User disabled',
Expand All @@ -82,14 +93,35 @@ const translations = {
resendSuccess: 'Resend success',
},
form: {
firstNameLabel: 'First name',
lastNameLabel: 'Last name',
emailLabel: 'Email',
passwordLabel: 'Password',
repeatPasswordLabel: 'Repeat password',
firstName: {
label: 'First name',
placeholder: 'James',
},
lastName: {
label: 'Last name',
placeholder: 'Smith',
},
email: {
label: 'Email',
placeholder: 'james.smith@eficode.com',
},
password: {
label: 'Password',
placeholder: 'Test1234',
forgot: 'Forgot?',
},
repeatPassword: {
label: 'Repeat password',
placeholder: 'Test1234',
},
roleLabel: 'Role',
maturityLabel: 'DevOps maturity',
buttonText: 'Signup',
buttonText: 'Sign up',
},
privacy: {
text:
'Eficode needs the contact information you provide to us to contact you about our products and services. You may unsubscribe from these communications at any time. For information on how to unsubscribe, as well as our privacy practices and commitment to protecting your privacy, please review our',
link: 'Privacy Policy',
},
errors: {
invalidEmail: 'Invalid email',
Expand Down
13 changes: 13 additions & 0 deletions packages/game-app/src/login/components/Login/Login.styled.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,17 @@ export const LoginForm = styled.div<{ error?: boolean }>`
${props => props.error && animations.shake()}
`;

export const Separator = styled.div`
width: 80px;
height: 0;
border: 1px solid #d7d2cb;
opacity: 1;
`;

export const PrivacySpan = styled.span`
font-size: 12px;
`;

LoginForm.displayName = 'LoginForm';
Separator.displayName = 'Separator';
PrivacySpan.displayName = 'PrivacySpan';
36 changes: 33 additions & 3 deletions packages/game-app/src/login/components/Login/Login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import { useTranslate } from '@pipeline/i18n';
import { useLogin } from '@pipeline/auth';
import { useLocation } from 'react-router-dom';
import { RoutingPath, useNavigateTo } from '@pipeline/routing';
import { LoginForm } from './Login.styled';
import { LoginForm, PrivacySpan, Separator } from './Login.styled';
import useNavigateOutsideTo from '../../../_shared/routing/useNavigateOutsideTo';

type Props = {};

Expand All @@ -30,6 +31,16 @@ const Login: React.FC<Props> = () => {
const location = useLocation<{ desiredUrl: string }>();

const goToSignUp = useNavigateTo(RoutingPath.Signup, location.state);
const goToForgotPassword = useNavigateTo(RoutingPath.ForgotPassword);

const openPrivacyPolicy = useNavigateOutsideTo('https://www.eficode.com');

const passwordProps = useMemo(() => {
return {
onForgotPassword: goToForgotPassword,
forgotPasswordLabel: t('login.form.password.forgot'),
};
}, [t, goToForgotPassword]);

return (
<TowColumnPage
Expand All @@ -39,15 +50,34 @@ const Login: React.FC<Props> = () => {
<Box mt={5}>
<FormProvider {...methods}>
<form onSubmit={submit}>
<FormTextField name="email" label={t('login.form.emailLabel')} />
<FormTextField
name="email"
label={t('login.form.email.label')}
placeholder={t('login.form.email.placeholder')}
/>
<Box mt={3}>
<FormTextField CustomInput={PasswordInput} name="password" label={t('login.form.passwordLabel')} />
<FormTextField
CustomInput={PasswordInput}
name="password"
label={t('login.form.password.label')}
placeholder={t('login.form.password.placeholder')}
others={passwordProps}
/>
</Box>
<Box textAlign="center" mt={5}>
<Button type="submit" label={t('login.form.buttonText')} loading={loginLoading} onClick={submit} />
</Box>
{loginTranslateError ? <ErrorMessage message={loginTranslateError} /> : null}
<Box mt={4} textAlign="center">
<PrivacySpan>{t('login.privacy.text')}</PrivacySpan>
<Link onClick={openPrivacyPolicy} fontSize="12px">
{t('login.privacy.link')}
</Link>
</Box>
<Box display="flex" flexDirection="row" justifyContent="center" mt={4}>
<Separator />
</Box>
<Box mt={4} textAlign="center">
<span>{t('login.notYetAccount')}</span>&nbsp;
<Link onClick={goToSignUp}>{t('login.goToSignup')}</Link>
</Box>
Expand Down
Loading

0 comments on commit fc9140b

Please sign in to comment.