Skip to content

Commit

Permalink
Basic useToast hook plus success toast on addNewEditor modal
Browse files Browse the repository at this point in the history
  • Loading branch information
jamdelion committed Aug 29, 2024
1 parent f484485 commit 5ea971b
Show file tree
Hide file tree
Showing 10 changed files with 129 additions and 41 deletions.
30 changes: 30 additions & 0 deletions editor.planx.uk/src/components/Toast/Toast.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import Alert from "@mui/material/Alert";
import Snackbar from "@mui/material/Snackbar";
import { useToast } from "hooks/useToast";
import React from "react";

interface ToastProps {
message: string;
type: ToastType;
id: number;
}

export type ToastType = "success" | "warning" | "info" | "error";

const Toast = ({ message, type = "success", id }: ToastProps) => {
const toast = useToast();

const handleCloseToast = () => {
toast.remove(id);
};

return (
<Snackbar onClose={handleCloseToast} autoHideDuration={6000} open={true}>
<Alert onClose={handleCloseToast} severity={type} sx={{ width: "100%" }}>
{message}
</Alert>
</Snackbar>
);
};

export default Toast;
15 changes: 15 additions & 0 deletions editor.planx.uk/src/components/Toast/ToastContainer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from "react";

import Toast from "./Toast";

const ToastContainer = ({ toasts }) => {
return (
<div className="toasts-container">
{toasts.map((toast) => (
<Toast key={toast.id} {...toast} />
))}
</div>
);
};

export default ToastContainer;
47 changes: 47 additions & 0 deletions editor.planx.uk/src/contexts/ToastContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { ToastType } from "components/Toast/Toast";
import ToastContainer from "components/Toast/ToastContainer";
import React, { createContext, ReactNode, useReducer } from "react";
import { toastReducer } from "reducers/toastReducer";

export const ToastContext = createContext(null);

const initialState = {
toasts: [],
};

export const ToastContextProvider = ({
children,
}: Readonly<{ children: ReactNode }>) => {
const [state, dispatch] = useReducer(toastReducer, initialState);
const addToast = (type: ToastType, message: string) => {
const id = Math.floor(Math.random() * 10_000_000);
dispatch({ type: "ADD_TOAST", payload: { id, message, type } });
};
const remove = (id: number) => {
dispatch({ type: "DELETE_TOAST", payload: id });
};
const success = (message: string) => {
addToast("success", message);
};

const warning = (message: string) => {
addToast("warning", message);
};

const info = (message: string) => {
addToast("info", message);
};

const error = (message: string) => {
addToast("error", message);
};

const value = { success, warning, info, error, remove };

return (
<ToastContext.Provider value={value}>
<ToastContainer toasts={state.toasts} />
{children}
</ToastContext.Provider>
);
};
5 changes: 5 additions & 0 deletions editor.planx.uk/src/hooks/useToast.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { useContext } from "react";

import { ToastContext } from "../contexts/ToastContext";

export const useToast = () => useContext(ToastContext);
6 changes: 3 additions & 3 deletions editor.planx.uk/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { ApolloProvider } from "@apollo/client";
import CssBaseline from "@mui/material/CssBaseline";
import { StyledEngineProvider, ThemeProvider } from "@mui/material/styles";
import { MyMap } from "@opensystemslab/map";
import { ToastContextProvider } from "contexts/ToastContext";
import { getCookie, setCookie } from "lib/cookie";
import ErrorPage from "pages/ErrorPage";
import { AnalyticsProvider } from "pages/FlowEditor/lib/analytics/provider";
Expand All @@ -16,7 +17,6 @@ import HelmetProvider from "react-navi-helmet-async";
import { ToastContainer } from "react-toastify";

// init airbrake before everything else
import * as airbrake from "./airbrake";
import DelayedLoadingIndicator from "./components/DelayedLoadingIndicator";
import { client } from "./lib/graphql";
import navigation from "./lib/navigation";
Expand Down Expand Up @@ -93,7 +93,7 @@ const Layout: React.FC<{
};

root.render(
<>
<ToastContextProvider>
<ApolloProvider client={client}>
<AnalyticsProvider>
<Router context={{ currentUser: hasJWT() }} navigation={navigation}>
Expand All @@ -109,5 +109,5 @@ root.render(
</AnalyticsProvider>
</ApolloProvider>
<ToastContainer icon={false} theme="colored" />
</>,
</ToastContextProvider>,
);
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import DialogActions from "@mui/material/DialogActions";
import DialogContent from "@mui/material/DialogContent";
import Typography from "@mui/material/Typography";
import { FormikHelpers, useFormik } from "formik";
import { useToast } from "hooks/useToast";
import { useStore } from "pages/FlowEditor/lib/store";
import React, { useState } from "react";
import InputGroup from "ui/editor/InputGroup";
Expand All @@ -24,11 +25,12 @@ import { optimisticallyUpdateMembersTable } from "./lib/optimisticallyUpdateMemb
export const AddNewEditorModal = ({
showModal,
setShowModal,
setShowToast,
}: AddNewEditorModalProps) => {
const [showUserAlreadyExistsError, setShowUserAlreadyExistsError] =
useState<boolean>(false);

const toast = useToast();

const clearErrors = () => {
setShowUserAlreadyExistsError(false);
};
Expand Down Expand Up @@ -58,7 +60,7 @@ export const AddNewEditorModal = ({
clearErrors();
optimisticallyUpdateMembersTable(values, newUserId);
setShowModal(false);
setShowToast(true);
toast.success("Successfully added a user");
resetForm({ values });
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import Alert from "@mui/material/Alert";
import Chip from "@mui/material/Chip";
import Snackbar from "@mui/material/Snackbar";
import Table from "@mui/material/Table";
import TableBody from "@mui/material/TableBody";
import TableCell from "@mui/material/TableCell";
Expand All @@ -20,18 +18,6 @@ export const MembersTable = ({
showAddMemberButton,
}: MembersTableProps) => {
const [showModal, setShowModal] = useState(false);
const [showToast, setShowToast] = useState(false);

const handleCloseToast = (
_event?: React.SyntheticEvent | Event,
reason?: string,
) => {
if (reason === "clickaway") {
return;
}

setShowToast(false);
};

const roleLabels: Record<string, string> = {
platformAdmin: "Admin",
Expand Down Expand Up @@ -113,28 +99,9 @@ export const MembersTable = ({
)}
</TableBody>
</Table>
{showAddMemberButton && (
<Snackbar
open={showToast}
autoHideDuration={6000}
onClose={handleCloseToast}
>
<Alert
onClose={handleCloseToast}
severity="success"
sx={{ width: "100%" }}
>
Successfully added a user
</Alert>
</Snackbar>
)}
</TableContainer>
{showModal && (
<AddNewEditorModal
setShowToast={setShowToast}
showModal={showModal}
setShowModal={setShowModal}
/>
<AddNewEditorModal showModal={showModal} setShowModal={setShowModal} />
)}
</>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { screen } from "@testing-library/react";
import { ToastContextProvider } from "contexts/ToastContext";
import React from "react";
import { DndProvider } from "react-dnd";
import { HTML5Backend } from "react-dnd-html5-backend";
Expand All @@ -9,7 +10,9 @@ import { TeamMembers } from "../../TeamMembers";
export const setupTeamMembersScreen = async () => {
const { user } = setup(
<DndProvider backend={HTML5Backend}>
<TeamMembers />
<ToastContextProvider>
<TeamMembers />
</ToastContextProvider>
</DndProvider>,
);
await screen.findByText("Team editors");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ export interface MembersTableProps {
export interface AddNewEditorModalProps {
showModal: boolean;
setShowModal: React.Dispatch<SetStateAction<boolean>>;
setShowToast: React.Dispatch<SetStateAction<boolean>>;
}

export interface AddNewEditorFormValues {
Expand Down
20 changes: 20 additions & 0 deletions editor.planx.uk/src/reducers/toastReducer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
export const toastReducer = (state, action) => {
switch (action.type) {
case "ADD_TOAST":
return {
...state,
toasts: [...state.toasts, action.payload],
};
case "DELETE_TOAST": {
const updatedToasts = state.toasts.filter(
(toast) => toast.id !== action.payload,
);
return {
...state,
toasts: updatedToasts,
};
}
default:
throw new Error(`Unhandled action type: ${action.type}`);
}
};

0 comments on commit 5ea971b

Please sign in to comment.