Skip to content

Commit

Permalink
feat: 🎸 Adding loading state to dialogs
Browse files Browse the repository at this point in the history
✅ Closes: #70
  • Loading branch information
Chris-Boyle committed Jan 13, 2021
1 parent 5161f99 commit 0f4d01d
Show file tree
Hide file tree
Showing 11 changed files with 219 additions and 13 deletions.
31 changes: 31 additions & 0 deletions src/components/LoadingIcon/LoadingIcon.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
.sqLoadingIcon {
opacity: 0.65; /* Per Chris Nemeth, make this less intense */
}

.sqLoadingIcon__brown1 {
animation: bumpit 2s linear 0s infinite;
}
.sqLoadingIcon__orange2,
.sqLoadingIcon__orange1 {
animation: bumpit 2s linear 0.66s infinite;
}

.sqLoadingIcon__yellow2,
.sqLoadingIcon__yellow1 {
animation: bumpit 2s linear 1.33s infinite;
}

@keyframes bumpit {
0% {
transform: scale(1);
}
10% {
transform: scale(1);
}
60% {
transform: scale(0.9) translate(20px, 20px);
}
100% {
transform: scale(1);
}
}
53 changes: 53 additions & 0 deletions src/components/LoadingIcon/LoadingIcon.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import React from 'react';
import './LoadingIcon.css';

const LoadingIcon = props => (
<svg height={props.height || '10rem'} viewBox="0 0 446 439">
<g className="sqLoadingIcon" stroke="none" strokeWidth="1" fill="none">
<circle
className="sqLoadingIcon__orange2"
stroke="#EC9252"
strokeWidth="35"
cx="250.5"
cy="231.5"
r="177.5"
/>
<circle
className="sqLoadingIcon__yellow2"
stroke="#FFE18B"
strokeWidth="35"
cx="195.5"
cy="243.5"
r="177.5"
/>
<circle
className="sqLoadingIcon__brown1"
stroke="#665736"
strokeWidth="35"
cx="210.5"
cy="195.5"
r="177.5"
/>
<circle
className="sqLoadingIcon__yellow1"
stroke="#FFE18B"
strokeWidth="35"
opacity="0.72"
cx="195.5"
cy="243.5"
r="177.5"
/>
<circle
className="sqLoadingIcon__orange1"
stroke="#EC9252"
strokeWidth="35"
opacity="0.63"
cx="250.5"
cy="231.5"
r="177.5"
/>
</g>
</svg>
);

export default LoadingIcon;
1 change: 1 addition & 0 deletions src/components/LoadingIcon/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {default} from './LoadingIcon';
7 changes: 7 additions & 0 deletions src/components/LoadingSpinner/LoadingSpinner.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.loadingSpinner {
display: flex;
flex: 1 1 auto;
flex-direction: column;
width: 100%;
animation: 0.45s fade-in;
}
21 changes: 21 additions & 0 deletions src/components/LoadingSpinner/LoadingSpinner.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from 'react';
import PropTypes from 'prop-types';

import LoadingIcon from '../LoadingIcon';
import './LoadingSpinner.css';

function LoadingSpinner({message}) {
return (
<div className="loadingSpinner flex--centered">
<LoadingIcon height="10rem" />
{message && <h3>{message}</h3>}
</div>
);
}

LoadingSpinner.propTypes = {
isLoading: PropTypes.bool,
message: PropTypes.string
};

export default LoadingSpinner;
1 change: 1 addition & 0 deletions src/components/LoadingSpinner/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {default} from './LoadingSpinner';
6 changes: 5 additions & 1 deletion src/components/SQFormDialog/SQFormDialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ function SQFormDialog({
enableReinitialize = false,
initialValues,
muiGridProps = {},
validationSchema
validationSchema,
isLoading
}) {
const validationYupSchema = React.useMemo(() => {
if (!validationSchema) return;
Expand All @@ -35,6 +36,7 @@ function SQFormDialog({
validateOnMount={true}
>
<SQFormDialogInner
isLoading={isLoading}
cancelButtonText={cancelButtonText}
children={children}
disableBackdropClick={disableBackdropClick}
Expand All @@ -60,6 +62,8 @@ SQFormDialog.propTypes = {
disableBackdropClick: PropTypes.bool,
/** The current disabled state of the Dialog Save Button */
isDisabled: PropTypes.bool,
/** Should the loading spinner be shown */
isLoading: PropTypes.bool,
/** The current open/closed state of the Dialog */
isOpen: PropTypes.bool.isRequired,
/** Determine the max-width of the dialog. The dialog width grows with the size of the screen. Set to false to disable maxWidth. */
Expand Down
20 changes: 16 additions & 4 deletions src/components/SQFormDialog/SQFormDialogInner.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {Form} from 'formik';
import {RoundedButton} from 'scplus-shared-components';
import {useSQFormContext} from '../../index';
import SQFormButton from '../SQForm/SQFormButton';
import LoadingSpinner from '../LoadingSpinner';

const Transition = React.forwardRef((props, ref) => {
return <Slide direction="down" ref={ref} {...props} />;
Expand All @@ -39,7 +40,8 @@ function SQFormDialogInner({
onSave,
saveButtonText,
title,
muiGridProps
muiGridProps,
isLoading
}) {
const actionsClasses = useActionsStyles();
const {resetForm} = useSQFormContext();
Expand All @@ -62,9 +64,17 @@ function SQFormDialogInner({
<Typography variant="h4">{title}</Typography>
</DialogTitle>
<DialogContent dividers={true}>
<Grid {...muiGridProps} container spacing={muiGridProps.spacing || 2}>
{children}
</Grid>
{isLoading ? (
<LoadingSpinner />
) : (
<Grid
{...muiGridProps}
container
spacing={muiGridProps.spacing || 2}
>
{children}
</Grid>
)}
</DialogContent>
<DialogActions classes={actionsClasses}>
<RoundedButton
Expand Down Expand Up @@ -93,6 +103,8 @@ SQFormDialogInner.propTypes = {
disableBackdropClick: PropTypes.bool,
/** The current disabled state of the Dialog Save Button */
isDisabled: PropTypes.bool,
/** Should the loading spinner be shown */
isLoading: PropTypes.bool,
/** The current open/closed state of the Dialog */
isOpen: PropTypes.bool.isRequired,
/** Determine the max-width of the dialog. The dialog width grows with the size of the screen. Set to false to disable maxWidth. */
Expand Down
24 changes: 16 additions & 8 deletions src/components/SQFormDialogStepper/SQFormDialogStepper.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
import * as Yup from 'yup';
import {Form, Formik, useFormikContext} from 'formik';
import {RoundedButton} from 'scplus-shared-components';
import LoadingSpinner from '../LoadingSpinner';

export function SQFormDialogStep({children}) {
return <>{children}</>;
Expand Down Expand Up @@ -75,6 +76,7 @@ export function SQFormDialogStepper({
fullWidth = true,
contentStyle,
initialValues,
isLoading,
...props
}) {
const steps = React.Children.toArray(children);
Expand Down Expand Up @@ -220,14 +222,18 @@ export function SQFormDialogStepper({
...contentStyle
}}
>
<Grid
{...muiGridProps}
container
spacing={muiGridProps.spacing || 3}
justify="center"
>
{currentChild}
</Grid>
{isLoading ? (
<LoadingSpinner />
) : (
<Grid
{...muiGridProps}
container
spacing={muiGridProps.spacing || 3}
justify="center"
>
{currentChild}
</Grid>
)}
</DialogContent>
<DialogActions classes={actionsClasses}>
<RoundedButton
Expand Down Expand Up @@ -256,6 +262,8 @@ SQFormDialogStepper.propTypes = {
disableBackdropClick: PropTypes.bool,
/** Sets the dialog to the maxWidth. */
fullWidth: PropTypes.bool,
/** Should the loading spinner be shown */
isLoading: PropTypes.bool,
/** The current open/closed state of the Dialog */
isOpen: PropTypes.bool.isRequired,
/** Allows the initial values to be updated after initial render */
Expand Down
31 changes: 31 additions & 0 deletions stories/SQFormDialog.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,34 @@ export const sqFormDialogWithAutoFocus = () => (
</SQFormDialog>
</>
);

export const sqFormDialogLoading = () => (
<>
<h1>Click the Knobs tab below to toggle the open state of the Dialog</h1>
<SQFormDialog
isOpen={boolean('isOpen', false, 'Open/Close Dialog')}
isDisabled={boolean(
'isDisabled',
false,
'Toggle disabled state of Save Button'
)}
maxWidth="sm"
onClose={action('Close button clicked')}
onSave={handleSubmit}
title="Create New User"
initialValues={MOCK_EMPTY_USER_STATE}
muiGridProps={{
spacing: 2,
alignItems: 'center'
}}
isLoading={true}
>
<SQFormTextField
name="firstName"
label="First Name (Auto Focused)"
muiFieldProps={{autoFocus: true}}
/>
<SQFormTextField name="lastName" label="Last Name" />
</SQFormDialog>
</>
);
37 changes: 37 additions & 0 deletions stories/SQFormDialogStepper.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -294,3 +294,40 @@ export const SQDialogStepperWithValidation = () => {
</>
);
};

export const SQDialogStepperLoading = () => {
return (
<>
<h3>Toggle the isOpen checkbox in the Knobs tab to view the Stepper</h3>
<SQFormDialogStepper
title="SQ Stepper Form"
isOpen={boolean('isOpen', false, 'Open/Close Dialog')}
onClose={action('Close button clicked')}
fullWidth
maxWidth="md"
initialValues={{
firstName: ''
}}
setValues={formValues => {
console.log('values set', formValues);
}}
onSubmit={handleSubmit}
isLoading={true}
>
<SQFormDialogStep
label="Personal Data"
validationSchema={{
firstName: Yup.string().required('Required')
}}
>
<SQFormTextField
fullWidth
name="firstName"
label="First Name"
isRequired={true}
/>
</SQFormDialogStep>
</SQFormDialogStepper>
</>
);
};

0 comments on commit 0f4d01d

Please sign in to comment.