From 94aa86e4db24fe68921d0e22cc4d4dc1831f4c0e Mon Sep 17 00:00:00 2001 From: "Ibrahima G. Coulibaly" Date: Fri, 21 Jun 2024 22:35:56 +0100 Subject: [PATCH] fix: text split try catch --- package-lock.json | 38 ++++++++++++++ package.json | 1 + src/components/App.tsx | 49 +++++++++++------- src/components/result/ToolTextResult.tsx | 22 +++++--- src/config/muiConfig.ts | 9 ++-- src/contexts/CustomSnackBarContext.tsx | 35 +++++++++++++ src/pages/string/split/index.tsx | 64 +++++++++++++----------- 7 files changed, 161 insertions(+), 57 deletions(-) create mode 100644 src/contexts/CustomSnackBarContext.tsx diff --git a/package-lock.json b/package-lock.json index fee9f1e..aa56a82 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "@types/lodash": "^4.17.5", "formik": "^2.4.6", "lodash": "^4.17.21", + "notistack": "^3.0.1", "react": "^18.3.1", "react-dom": "^18.3.1", "react-router-dom": "^6.23.1", @@ -4393,6 +4394,14 @@ "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==", "dev": true }, + "node_modules/goober": { + "version": "2.1.14", + "resolved": "https://registry.npmjs.org/goober/-/goober-2.1.14.tgz", + "integrity": "sha512-4UpC0NdGyAFqLNPnhCT2iHpza2q+RAY3GV85a/mRPdzyPQMsj0KmMMuetdIkzWRbJ+Hgau1EZztq8ImmiMGhsg==", + "peerDependencies": { + "csstype": "^3.0.10" + } + }, "node_modules/gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", @@ -5417,6 +5426,35 @@ "node": ">=0.10.0" } }, + "node_modules/notistack": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/notistack/-/notistack-3.0.1.tgz", + "integrity": "sha512-ntVZXXgSQH5WYfyU+3HfcXuKaapzAJ8fBLQ/G618rn3yvSzEbnOB8ZSOwhX+dAORy/lw+GC2N061JA0+gYWTVA==", + "dependencies": { + "clsx": "^1.1.0", + "goober": "^2.0.33" + }, + "engines": { + "node": ">=12.0.0", + "npm": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/notistack" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/notistack/node_modules/clsx": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", + "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", + "engines": { + "node": ">=6" + } + }, "node_modules/npm-run-path": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", diff --git a/package.json b/package.json index e6a8d16..062da09 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "@types/lodash": "^4.17.5", "formik": "^2.4.6", "lodash": "^4.17.21", + "notistack": "^3.0.1", "react": "^18.3.1", "react-dom": "^18.3.1", "react-router-dom": "^6.23.1", diff --git a/src/components/App.tsx b/src/components/App.tsx index 11f665a..ee7e794 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -1,26 +1,41 @@ -import { BrowserRouter, useRoutes } from 'react-router-dom' -import routesConfig from '../config/routesConfig' -import Navbar from './Navbar' -import { Suspense } from 'react' -import Loading from './Loading' -import { ThemeProvider } from '@mui/material' -import theme from '../config/muiConfig' +import { BrowserRouter, useRoutes } from 'react-router-dom'; +import routesConfig from '../config/routesConfig'; +import Navbar from './Navbar'; +import { Suspense } from 'react'; +import Loading from './Loading'; +import { ThemeProvider } from '@mui/material'; +import theme from '../config/muiConfig'; +import { CustomSnackBarProvider } from '../contexts/CustomSnackBarContext'; +import { SnackbarProvider } from 'notistack'; const AppRoutes = () => { - return useRoutes(routesConfig) -} + return useRoutes(routesConfig); +}; function App() { return ( - - - }> - - - + + + + + }> + + + + + - ) + ); } -export default App +export default App; diff --git a/src/components/result/ToolTextResult.tsx b/src/components/result/ToolTextResult.tsx index 916b132..6b14acf 100644 --- a/src/components/result/ToolTextResult.tsx +++ b/src/components/result/ToolTextResult.tsx @@ -1,11 +1,17 @@ -import Typography from '@mui/material/Typography' -import { Box, Stack, TextField } from '@mui/material' -import Button from '@mui/material/Button' -import DownloadIcon from '@mui/icons-material/Download' -import ContentPasteIcon from '@mui/icons-material/ContentPaste' -import React from 'react' +import Typography from '@mui/material/Typography'; +import { Box, Stack, TextField } from '@mui/material'; +import Button from '@mui/material/Button'; +import DownloadIcon from '@mui/icons-material/Download'; +import ContentPasteIcon from '@mui/icons-material/ContentPaste'; +import React from 'react'; -export default function ToolTextResult({ title = 'Result', value }: { title?: string; value: string }) { +export default function ToolTextResult({ + title = 'Result', + value +}: { + title?: string; + value: string; +}) { return ( @@ -17,5 +23,5 @@ export default function ToolTextResult({ title = 'Result', value }: { title?: st - ) + ); } diff --git a/src/config/muiConfig.ts b/src/config/muiConfig.ts index 0aeb41b..118efef 100644 --- a/src/config/muiConfig.ts +++ b/src/config/muiConfig.ts @@ -1,4 +1,4 @@ -import { createTheme } from '@mui/material' +import { createTheme } from '@mui/material'; const theme = createTheme({ typography: { @@ -6,7 +6,8 @@ const theme = createTheme({ textTransform: 'none' } }, - palette: { background: { default: '#ebf5ff' } } -}) + palette: { background: { default: '#ebf5ff' } }, + zIndex: { snackbar: 100000 } +}); -export default theme +export default theme; diff --git a/src/contexts/CustomSnackBarContext.tsx b/src/contexts/CustomSnackBarContext.tsx new file mode 100644 index 0000000..0fa50c6 --- /dev/null +++ b/src/contexts/CustomSnackBarContext.tsx @@ -0,0 +1,35 @@ +import { createContext, FC, ReactNode } from 'react'; +import { Zoom } from '@mui/material'; +import { useSnackbar } from 'notistack'; + +type CustomSnackBarContext = { + showSnackBar: (message: string, type: 'error' | 'success') => void; +}; + +// eslint-disable-next-line @typescript-eslint/no-redeclare +export const CustomSnackBarContext = createContext( + {} as CustomSnackBarContext +); + +interface Props { + children: ReactNode; +} + +export const CustomSnackBarProvider: FC = ({ children }) => { + const { enqueueSnackbar } = useSnackbar(); + const showSnackBar = (message: string, type: 'error' | 'success') => { + enqueueSnackbar(message, { + variant: type, + anchorOrigin: { + vertical: 'top', + horizontal: 'right' + }, + TransitionComponent: Zoom + }); + }; + return ( + + {children} + + ); +}; diff --git a/src/pages/string/split/index.tsx b/src/pages/string/split/index.tsx index ff04303..9e11ded 100644 --- a/src/pages/string/split/index.tsx +++ b/src/pages/string/split/index.tsx @@ -3,13 +3,14 @@ import ToolLayout from '../../../components/ToolLayout'; import { Box, Stack, TextField } from '@mui/material'; import Grid from '@mui/material/Grid'; import Typography from '@mui/material/Typography'; -import React, { useEffect, useRef, useState } from 'react'; +import React, { useContext, useEffect, useRef, useState } from 'react'; import ToolTextInput from '../../../components/input/ToolTextInput'; import ToolTextResult from '../../../components/result/ToolTextResult'; import { Field, Formik, FormikProps, useFormikContext } from 'formik'; import * as Yup from 'yup'; import ToolOptions from '../../../components/ToolOptions'; import { splitIntoChunks, splitTextByLength } from './service'; +import { CustomSnackBarContext } from '../../../contexts/CustomSnackBarContext'; type SplitOperatorType = 'symbol' | 'regex' | 'length' | 'chunks'; const initialValues = { @@ -142,38 +143,45 @@ export default function SplitText() { const [input, setInput] = useState(''); const [result, setResult] = useState(''); const formRef = useRef>(null); + const { showSnackBar } = useContext(CustomSnackBarContext); + const FormikListenerComponent = () => { const { values } = useFormikContext(); useEffect(() => { - const { - splitSeparatorType, - outputSeparator, - charBeforeChunk, - charAfterChunk, - chunksValue, - symbolValue, - regexValue, - lengthValue - } = values; - let splitText; - switch (splitSeparatorType) { - case 'symbol': - splitText = input.split(symbolValue); - break; - case 'regex': - splitText = input.split(new RegExp(regexValue)); - break; - case 'length': - splitText = splitTextByLength(input, Number(lengthValue)); - break; - case 'chunks': - splitText = splitIntoChunks(input, Number(chunksValue)).map( - (chunk) => `${charBeforeChunk}${chunk}${charAfterChunk}` - ); + try { + const { + splitSeparatorType, + outputSeparator, + charBeforeChunk, + charAfterChunk, + chunksValue, + symbolValue, + regexValue, + lengthValue + } = values; + let splitText; + switch (splitSeparatorType) { + case 'symbol': + splitText = input.split(symbolValue); + break; + case 'regex': + splitText = input.split(new RegExp(regexValue)); + break; + case 'length': + splitText = splitTextByLength(input, Number(lengthValue)); + break; + case 'chunks': + splitText = splitIntoChunks(input, Number(chunksValue)).map( + (chunk) => `${charBeforeChunk}${chunk}${charAfterChunk}` + ); + } + const res = splitText.join(outputSeparator); + setResult(res); + } catch (exception: unknown) { + if (exception instanceof Error) + showSnackBar(exception.message, 'error'); } - const res = splitText.join(outputSeparator); - setResult(res); }, [values, input]); return null; // This component doesn't render anything