Skip to content

Commit

Permalink
74 Add user modal (#75)
Browse files Browse the repository at this point in the history
* fab changes

* progress bar

* remove unused page components

* focus

* remove onCancel from user form

* remove onCancel globally

* tests

* fixes
  • Loading branch information
mwarman authored Sep 6, 2024
1 parent 6c8cd41 commit 93b2b7f
Show file tree
Hide file tree
Showing 15 changed files with 237 additions and 201 deletions.
3 changes: 3 additions & 0 deletions src/common/components/Icon/Icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
faUser,
faUserGear,
faUsers,
faXmark,
} from '@fortawesome/free-solid-svg-icons';
import classNames from 'classnames';

Expand Down Expand Up @@ -56,6 +57,7 @@ export enum IconName {
User = 'user',
Users = 'users',
UserGear = 'user_gear',
Xmark = 'xmark',
}

/**
Expand All @@ -78,6 +80,7 @@ const icons: Record<IconName, IconProp> = {
user_gear: faUserGear,
user: faUser,
users: faUsers,
xmark: faXmark,
};

/**
Expand Down
4 changes: 0 additions & 4 deletions src/common/components/Router/TabNavigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import HomePage from 'pages/Home/HomePage';
import UserDetailPage from 'pages/Users/components/UserDetail/UserDetailPage';
import UserListPage from 'pages/Users/components/UserList/UserListPage';
import UserEditPage from 'pages/Users/components/UserEdit/UserEditPage';
import UserAddPage from 'pages/Users/components/UserAdd/UserAddPage';
import AccountPage from 'pages/Account/AccountPage';
import ProfilePage from 'pages/Account/components/Profile/ProfilePage';
import DiagnosticsPage from 'pages/Account/components/Diagnostics/DiagnosticsPage';
Expand Down Expand Up @@ -48,9 +47,6 @@ const TabNavigation = (): JSX.Element => {
<Route exact path="/tabs/users/:userId/edit">
<UserEditPage />
</Route>
<Route exact path="/tabs/users/add">
<UserAddPage />
</Route>
<Route exact path="/tabs/account">
<AccountPage />
</Route>
Expand Down
77 changes: 0 additions & 77 deletions src/pages/Users/components/UserAdd/UserAdd.tsx

This file was deleted.

2 changes: 1 addition & 1 deletion src/pages/Users/components/UserAdd/UserAddFab.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.fab-user-add {
.ls-fab-user-add {
.icon {
font-size: 1.125rem;
}
Expand Down
27 changes: 19 additions & 8 deletions src/pages/Users/components/UserAdd/UserAddFab.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,42 @@
import { ComponentPropsWithoutRef } from 'react';
import { IonFab, IonFabButton } from '@ionic/react';
import classNames from 'classnames';

import './UserAddFab.scss';
import { BaseComponentProps } from 'common/components/types';
import { PropsWithTestId } from 'common/components/types';
import Icon, { IconName } from 'common/components/Icon/Icon';

/**
* Properties for the `UserAddFab` component.
* @see {@link PropsWithTestId}
* @see {@link IonFab}
*/
interface UserAddFabProps extends BaseComponentProps {}
interface UserAddFabProps extends PropsWithTestId, ComponentPropsWithoutRef<typeof IonFab> {}

/**
* The `UserAddFab` renders an Ionic Floating Action Button, or FAB.
* The button navigates to the create new `User` form when clicked.
* @param {UserAddFabProps} props - Component properties.
* @returns {JSX.Element} JSX
*/
const UserAddFab = ({ className, testid = 'fab-user-add' }: UserAddFabProps): JSX.Element => {
const UserAddFab = ({
className,
horizontal = 'end',
slot = 'fixed',
testid = 'fab-user-add',
vertical = 'bottom',
...fabProps
}: UserAddFabProps): JSX.Element => {
return (
<IonFab
className={classNames('fab-user-add', className)}
className={classNames('ls-fab-user-add', className)}
data-testid={testid}
slot="fixed"
vertical="bottom"
horizontal="end"
slot={slot}
vertical={vertical}
horizontal={horizontal}
{...fabProps}
>
<IonFabButton routerLink="/tabs/users/add">
<IonFabButton>
<Icon icon={IconName.Plus} />
</IonFabButton>
</IonFab>
Expand Down
116 changes: 116 additions & 0 deletions src/pages/Users/components/UserAdd/UserAddModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import { IonModalCustomEvent } from '@ionic/core';
import { OverlayEventDetail } from '@ionic/react/dist/types/components/react-component-lib/interfaces';
import {
IonButton,
IonButtons,
IonContent,
IonHeader,
IonModal,
IonProgressBar,
IonTitle,
IonToolbar,
useIonRouter,
} from '@ionic/react';
import { ComponentPropsWithoutRef, useState } from 'react';

import { PropsWithTestId } from 'common/components/types';
import { useCreateUser } from 'pages/Users/api/useCreateUser';
import { useProgress } from 'common/hooks/useProgress';
import { useToasts } from 'common/hooks/useToasts';
import { DismissButton } from 'common/components/Toast/Toast';
import Icon, { IconName } from 'common/components/Icon/Icon';
import UserForm from '../UserForm/UserForm';
import ErrorCard from 'common/components/Card/ErrorCard';

/**
* Properties for the `UserAddModal` component.
* @param {function} setIsOpen - A setter function which accepts a `boolean` that
* indicates if the modal should be open (`true`) or closed (`false`).
* @see {@link PropsWithTestId}
* @see {@link IonModal}
*/
interface UserAddModalProps extends PropsWithTestId, ComponentPropsWithoutRef<typeof IonModal> {
setIsOpen: (isOpen: boolean) => void;
}

/**
* The `UserAddModal` component renders an `IonModal` containing a Formik
* form to create a new `User`.
*
* @param {UserAddModalProps} props - Component properties.
* @returns {JSX.Element} JSX
*/
const UserAddModal = ({
onIonModalDidDismiss,
setIsOpen,
testid = 'modal-user-add',
...modalProps
}: UserAddModalProps): JSX.Element => {
const [error, setError] = useState<string>('');
const router = useIonRouter();
const { isActive: isActiveProgressBar, progressBar, setProgress } = useProgress();
const { createToast } = useToasts();
const { mutate: createUser } = useCreateUser();

const didDismiss = (e: IonModalCustomEvent<OverlayEventDetail>) => {
onIonModalDidDismiss?.(e);
setIsOpen(false);
};

return (
<IonModal onIonModalDidDismiss={didDismiss} {...modalProps} data-testid={testid}>
<IonHeader>
<IonToolbar>
<IonTitle>Add User</IonTitle>

<IonButtons slot="end">
<IonButton onClick={() => setIsOpen(false)} data-testid={`${testid}-button-close`}>
<Icon icon={IconName.Xmark} />
</IonButton>
</IonButtons>

{isActiveProgressBar && <IonProgressBar className="ion-hide-md-up" {...progressBar} />}
</IonToolbar>
</IonHeader>
<IonContent class="ion-padding">
{error && (
<ErrorCard
content={`We are experiencing problems processing your request. ${error}`}
className="ion-margin-bottom"
testid={`${testid}-error`}
/>
)}
<UserForm
onSubmit={(values, { setSubmitting }) => {
setProgress(true);
setError('');
createUser(
{ user: values },
{
onSuccess: (user) => {
setProgress(false);
setSubmitting(false);
createToast({
buttons: [DismissButton],
duration: 5000,
message: `${user.name} created`,
});
setIsOpen(false);
router.push(`/tabs/users/${user.id}`);
},
onError(error) {
setProgress(false);
setError(error.message);
setSubmitting(false);
},
},
);
}}
testid={`${testid}-form`}
/>
</IonContent>
</IonModal>
);
};

export default UserAddModal;
35 changes: 0 additions & 35 deletions src/pages/Users/components/UserAdd/UserAddPage.tsx

This file was deleted.

16 changes: 0 additions & 16 deletions src/pages/Users/components/UserAdd/__tests__/UserAdd.test.tsx

This file was deleted.

Loading

0 comments on commit 93b2b7f

Please sign in to comment.