Correctly center the spinner in buttons

This commit is contained in:
Dane Everitt 2020-07-04 14:34:43 -07:00
parent 8c20158e58
commit 1c97dd4e24
No known key found for this signature in database
GPG key ID: EEA66103B3D71F53
4 changed files with 40 additions and 51 deletions

View file

@ -5,12 +5,12 @@ import { httpErrorToHuman } from '@/api/http';
import LoginFormContainer from '@/components/auth/LoginFormContainer'; import LoginFormContainer from '@/components/auth/LoginFormContainer';
import { ActionCreator } from 'easy-peasy'; import { ActionCreator } from 'easy-peasy';
import { StaticContext } from 'react-router'; import { StaticContext } from 'react-router';
import Spinner from '@/components/elements/Spinner';
import { useFormikContext, withFormik } from 'formik'; import { useFormikContext, withFormik } from 'formik';
import { object, string } from 'yup';
import useFlash from '@/plugins/useFlash'; import useFlash from '@/plugins/useFlash';
import { FlashStore } from '@/state/flashes'; import { FlashStore } from '@/state/flashes';
import Field from '@/components/elements/Field'; import Field from '@/components/elements/Field';
import tw from 'twin.macro';
import Button from '@/components/elements/Button';
interface Values { interface Values {
code: string; code: string;
@ -29,13 +29,10 @@ const LoginCheckpointContainer = () => {
const [ isMissingDevice, setIsMissingDevice ] = useState(false); const [ isMissingDevice, setIsMissingDevice ] = useState(false);
return ( return (
<LoginFormContainer <LoginFormContainer title={'Device Checkpoint'} css={tw`w-full flex`}>
title={'Device Checkpoint'} <div css={tw`mt-6`}>
className={'w-full flex'}
>
<div className={'mt-6'}>
<Field <Field
light={true} light
name={isMissingDevice ? 'recoveryCode' : 'code'} name={isMissingDevice ? 'recoveryCode' : 'code'}
title={isMissingDevice ? 'Recovery Code' : 'Authentication Code'} title={isMissingDevice ? 'Recovery Code' : 'Authentication Code'}
description={ description={
@ -44,38 +41,35 @@ const LoginCheckpointContainer = () => {
: 'Enter the two-factor token generated by your device.' : 'Enter the two-factor token generated by your device.'
} }
type={isMissingDevice ? 'text' : 'number'} type={isMissingDevice ? 'text' : 'number'}
autoFocus={true} autoFocus
/> />
</div> </div>
<div className={'mt-6'}> <div css={tw`mt-6`}>
<button <Button
size={'xlarge'}
type={'submit'} type={'submit'}
className={'btn btn-primary btn-jumbo'}
disabled={isSubmitting} disabled={isSubmitting}
isLoading={isSubmitting}
> >
{isSubmitting ? Continue
<Spinner size={'small'} className={'mx-auto'}/> </Button>
:
'Continue'
}
</button>
</div> </div>
<div className={'mt-6 text-center'}> <div css={tw`mt-6 text-center`}>
<span <span
onClick={() => { onClick={() => {
setFieldValue('code', ''); setFieldValue('code', '');
setFieldValue('recoveryCode', ''); setFieldValue('recoveryCode', '');
setIsMissingDevice(s => !s); setIsMissingDevice(s => !s);
}} }}
className={'cursor-pointer text-xs text-neutral-500 tracking-wide uppercase no-underline hover:text-neutral-700'} css={tw`cursor-pointer text-xs text-neutral-500 tracking-wide uppercase no-underline hover:text-neutral-700`}
> >
{!isMissingDevice ? 'I\'ve Lost My Device' : 'I Have My Device'} {!isMissingDevice ? 'I\'ve Lost My Device' : 'I Have My Device'}
</span> </span>
</div> </div>
<div className={'mt-6 text-center'}> <div css={tw`mt-6 text-center`}>
<Link <Link
to={'/auth/login'} to={'/auth/login'}
className={'text-xs text-neutral-500 tracking-wide uppercase no-underline hover:text-neutral-700'} css={tw`text-xs text-neutral-500 tracking-wide uppercase no-underline hover:text-neutral-700`}
> >
Return to Login Return to Login
</Link> </Link>

View file

@ -36,11 +36,7 @@ const LoginContainer = ({ isSubmitting, setFieldValue, values, submitForm, handl
return ( return (
<React.Fragment> <React.Fragment>
{ref.current && ref.current.render()} {ref.current && ref.current.render()}
<LoginFormContainer <LoginFormContainer title={'Login to Continue'} css={tw`w-full flex`} onSubmit={submit}>
title={'Login to Continue'}
css={tw`w-full flex`}
onSubmit={submit}
>
<Field <Field
type={'text'} type={'text'}
label={'Username or Email'} label={'Username or Email'}

View file

@ -11,15 +11,16 @@ import Spinner from '@/components/elements/Spinner';
import { Formik, FormikHelpers } from 'formik'; import { Formik, FormikHelpers } from 'formik';
import { object, ref, string } from 'yup'; import { object, ref, string } from 'yup';
import Field from '@/components/elements/Field'; import Field from '@/components/elements/Field';
import Input from '@/components/elements/Input';
type Props = Readonly<RouteComponentProps<{ token: string }> & {}>; import tw from 'twin.macro';
import Button from '@/components/elements/Button';
interface Values { interface Values {
password: string; password: string;
passwordConfirmation: string; passwordConfirmation: string;
} }
export default ({ match, history, location }: Props) => { export default ({ match, location }: RouteComponentProps<{ token: string }>) => {
const [ email, setEmail ] = useState(''); const [ email, setEmail ] = useState('');
const { clearFlashes, addFlash } = useStoreActions((actions: Actions<ApplicationStore>) => actions.flashes); const { clearFlashes, addFlash } = useStoreActions((actions: Actions<ApplicationStore>) => actions.flashes);
@ -62,46 +63,43 @@ export default ({ match, history, location }: Props) => {
{({ isSubmitting }) => ( {({ isSubmitting }) => (
<LoginFormContainer <LoginFormContainer
title={'Reset Password'} title={'Reset Password'}
className={'w-full flex'} css={tw`w-full flex`}
> >
<div> <div>
<label>Email</label> <label>Email</label>
<input className={'input'} value={email} disabled={true}/> <Input value={email} light disabled/>
</div> </div>
<div className={'mt-6'}> <div css={tw`mt-6`}>
<Field <Field
light={true} light
label={'New Password'} label={'New Password'}
name={'password'} name={'password'}
type={'password'} type={'password'}
description={'Passwords must be at least 8 characters in length.'} description={'Passwords must be at least 8 characters in length.'}
/> />
</div> </div>
<div className={'mt-6'}> <div css={tw`mt-6`}>
<Field <Field
light={true} light
label={'Confirm New Password'} label={'Confirm New Password'}
name={'passwordConfirmation'} name={'passwordConfirmation'}
type={'password'} type={'password'}
/> />
</div> </div>
<div className={'mt-6'}> <div css={tw`mt-6`}>
<button <Button
size={'xlarge'}
type={'submit'} type={'submit'}
className={'btn btn-primary btn-jumbo'}
disabled={isSubmitting} disabled={isSubmitting}
isLoading={isSubmitting}
> >
{isSubmitting ? Reset Password
<Spinner size={'small'} className={'mx-auto'}/> </Button>
:
'Reset Password'
}
</button>
</div> </div>
<div className={'mt-6 text-center'}> <div css={tw`mt-6 text-center`}>
<Link <Link
to={'/auth/login'} to={'/auth/login'}
className={'text-xs text-neutral-500 tracking-wide no-underline uppercase hover:text-neutral-600'} css={tw`text-xs text-neutral-500 tracking-wide no-underline uppercase hover:text-neutral-600`}
> >
Return to Login Return to Login
</Link> </Link>

View file

@ -1,6 +1,7 @@
import React from 'react'; import React from 'react';
import styled, { css } from 'styled-components/macro'; import styled, { css } from 'styled-components/macro';
import tw from 'twin.macro'; import tw from 'twin.macro';
import Spinner from '@/components/elements/Spinner';
interface Props { interface Props {
isLoading?: boolean; isLoading?: boolean;
@ -10,7 +11,7 @@ interface Props {
} }
const StyledButton = styled.button<Omit<Props, 'isLoading'>>` const StyledButton = styled.button<Omit<Props, 'isLoading'>>`
${tw`rounded p-2 uppercase tracking-wide text-sm transition-all duration-150`}; ${tw`relative inline-block rounded p-2 uppercase tracking-wide text-sm transition-all duration-150`};
${props => props.isSecondary && css<Props>` ${props => props.isSecondary && css<Props>`
${tw`border border-neutral-600 bg-transparent text-neutral-200`}; ${tw`border border-neutral-600 bg-transparent text-neutral-200`};
@ -79,8 +80,8 @@ type ComponentProps = Omit<JSX.IntrinsicElements['button'], 'ref' | keyof Props>
const Button: React.FC<ComponentProps> = ({ children, isLoading, ...props }) => ( const Button: React.FC<ComponentProps> = ({ children, isLoading, ...props }) => (
<StyledButton {...props}> <StyledButton {...props}>
{isLoading && {isLoading &&
<div css={tw`w-full flex absolute justify-center`} style={{ marginLeft: '-0.75rem' }}> <div css={tw`flex absolute justify-center items-center w-full h-full left-0 top-0`}>
<div className={'spinner-circle spinner-white spinner-sm'}/> <Spinner size={'small'}/>
</div> </div>
} }
<span css={isLoading ? tw`text-transparent` : undefined}> <span css={isLoading ? tw`text-transparent` : undefined}>