diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 7046152..f0d1bb3 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -32,6 +32,7 @@ module.exports = { 'import/no-extraneous-dependencies': 0, 'react/function-component-definition': 0, 'no-underscore-dangle': 0, + 'jsx-a11y/no-static-element-interactions': 0, 'react-refresh/only-export-components': ['warn', { allowConstantExport: true }], 'sort-imports': ['error', {ignoreCase: true, ignoreDeclarationSort: true}], 'import/order': [ diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ac42a83..5c5153d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -5,7 +5,7 @@ on: types: [opened, synchronize] env: - NODE_VERSION: 18.16.0 + NODE_VERSION: 21.5.0 jobs: linting: diff --git a/package-lock.json b/package-lock.json index 6974718..fcb7981 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,6 @@ "@hookform/resolvers": "^3.3.2", "@lit/react": "^1.0.2", "@material/web": "^1.0.1", - "@types/react-test-renderer": "^18.0.7", "firebase": "^10.7.1", "overlayscrollbars": "^2.4.5", "overlayscrollbars-react": "^0.5.3", @@ -19,7 +18,7 @@ "react-dom": "^18.2.0", "react-hook-form": "^7.48.2", "react-router-dom": "^6.20.1", - "toastify-js": "^1.12.0", + "react-toastify": "^9.1.3", "yup": "^1.3.2" }, "devDependencies": { @@ -6402,24 +6401,6 @@ "thenify-all": "^1.0.0" } }, - "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -7006,6 +6987,24 @@ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "dev": true }, + "node_modules/postcss/node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -7307,6 +7306,26 @@ "react-dom": ">=16.8" } }, + "node_modules/react-toastify": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-9.1.3.tgz", + "integrity": "sha512-fPfb8ghtn/XMxw3LkxQBk3IyagNpF/LIKjOBflbexr2AWxAH1MJgvnESwEwBn9liLFXgTKWgBSdZpw9m4OTHTg==", + "dependencies": { + "clsx": "^1.1.1" + }, + "peerDependencies": { + "react": ">=16", + "react-dom": ">=16" + } + }, + "node_modules/react-toastify/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/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -8421,11 +8440,6 @@ "node": ">=8.0" } }, - "node_modules/toastify-js": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/toastify-js/-/toastify-js-1.12.0.tgz", - "integrity": "sha512-HeMHCO9yLPvP9k0apGSdPUWrUbLnxUKNFzgUoZp1PHCLploIX/4DSQ7V8H25ef+h4iO9n0he7ImfcndnN6nDrQ==" - }, "node_modules/toposort": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", diff --git a/package.json b/package.json index 7815150..df25dd4 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "react-dom": "^18.2.0", "react-hook-form": "^7.48.2", "react-router-dom": "^6.20.1", - "toastify-js": "^1.12.0", + "react-toastify": "^9.1.3", "yup": "^1.3.2" }, "devDependencies": { @@ -37,8 +37,8 @@ "@types/node": "^20.10.3", "@types/react": "^18.2.37", "@types/react-dom": "^18.2.15", - "@types/toastify-js": "^1.12.3", "@types/react-test-renderer": "^18.0.7", + "@types/toastify-js": "^1.12.3", "@typescript-eslint/eslint-plugin": "^6.13.2", "@typescript-eslint/parser": "^6.13.2", "@vitejs/plugin-react": "^4.2.0", diff --git a/src/app/App.tsx b/src/app/App.tsx index 489994f..d9c116b 100644 --- a/src/app/App.tsx +++ b/src/app/App.tsx @@ -1,10 +1,20 @@ +import { useEffect } from 'react'; + import { RouterProvider } from 'react-router-dom'; import router from '@/router/router'; +import localStorageKeys from '@/shared/constants/localStorageKeys'; +import colorThemeSwitcher from '@/shared/helpers/colorThemeSwitcher'; import AuthProvider from '@shared/Context/AuthContext'; import LanguageProvider from '@shared/Context/LanguageContext'; const App = () => { + useEffect(() => { + const isLightTheme = localStorage.getItem(localStorageKeys.LIGHT_THEME); + if (isLightTheme) { + colorThemeSwitcher.setLight(); + } + }, []); return ( diff --git a/src/components/DocsComp/ui/DocsOverlay.tsx b/src/components/DocsComp/ui/DocsOverlay.tsx index efcdb4b..f791017 100644 --- a/src/components/DocsComp/ui/DocsOverlay.tsx +++ b/src/components/DocsComp/ui/DocsOverlay.tsx @@ -13,7 +13,7 @@ type PropsType = { }; const DocsOverlay = ({ isShown, setIsDocsShown, explorer, children }: PropsType) => { - function closeHandler(e: React.MouseEvent) { + function closeHandler(e: React.MouseEvent | React.KeyboardEvent) { if ((e.target as HTMLButtonElement).hasAttribute('data-overlay')) { setIsDocsShown((prev) => !prev); explorer.setInitState(); @@ -23,15 +23,15 @@ const DocsOverlay = ({ isShown, setIsDocsShown, explorer, children }: PropsType) return null; } return ( - + ); }; diff --git a/src/components/DocsComp/ui/DocsRootComp.tsx b/src/components/DocsComp/ui/DocsRootComp.tsx index 3940070..3e51e76 100644 --- a/src/components/DocsComp/ui/DocsRootComp.tsx +++ b/src/components/DocsComp/ui/DocsRootComp.tsx @@ -29,7 +29,7 @@ const DocsRootComp = ({ types, explorer }: { types: SchemaTypeObj[]; explorer: D

Docs

-

A GraphQL schema provides a root type for each kind of operation.

+

A GraphQL schema provides a root type for each kind of operation.

Root types:

diff --git a/src/components/DocsComp/ui/DocsTypeComp.tsx b/src/components/DocsComp/ui/DocsTypeComp.tsx index cb5a134..3da0cbf 100644 --- a/src/components/DocsComp/ui/DocsTypeComp.tsx +++ b/src/components/DocsComp/ui/DocsTypeComp.tsx @@ -19,9 +19,9 @@ const DocsTypeComp = ({ explorer, currType }: { explorer: DocsExplorerType; curr const beforeSeparator =
; const afterSeparator = i >= field.args.length - 1 ?
: null; return ( - <> +

{separation && beforeSeparator} - + {arg.name}:  {before} clinkHandler(e, link)}> @@ -30,7 +30,7 @@ const DocsTypeComp = ({ explorer, currType }: { explorer: DocsExplorerType; curr {after} {separation && afterSeparator} - +

); }); const returnType = getTypeName(field.type); @@ -56,7 +56,7 @@ const DocsTypeComp = ({ explorer, currType }: { explorer: DocsExplorerType; curr }); const inputFields = currType?.inputFields?.map((field) => { return ( -
  • +
  • {field.name}:  = ({ onResponseOpen, isHidden, className }) => const query = readUrl(urlParams.QUERY); await navigator.clipboard.writeText(query); - toastifyNotation('Request copied!'); + toast(

    Request copied!

    ); }; return ( diff --git a/src/components/SettingsPageComp/ClearStorageComp.tsx b/src/components/SettingsPageComp/ClearStorageComp.tsx new file mode 100644 index 0000000..60a0b3b --- /dev/null +++ b/src/components/SettingsPageComp/ClearStorageComp.tsx @@ -0,0 +1,34 @@ +import { useState } from 'react'; + +import useLanguage from '@/shared/Context/hooks'; +import FilledTonalButton from '@/shared/ui/FilledTonalButton'; + +import ConfirmModal from './ConfirmModal'; +import ConfirmOverlay from './ConfirmOverlay'; + +const ClearStorageComp = () => { + const [isModalShown, setIsModalShown] = useState(false); + const { translation } = useLanguage(); + + return ( + <> +
    +
    +

    {translation.settingsPage.clear.title}

    +

    {translation.settingsPage.clear.subtitle}

    +
    + setIsModalShown((prev) => !prev)}> + {translation.settingsPage.clear.btn} + +
    + + + + + ); +}; + +export default ClearStorageComp; diff --git a/src/components/SettingsPageComp/ClearUndo.tsx b/src/components/SettingsPageComp/ClearUndo.tsx new file mode 100644 index 0000000..b5d449e --- /dev/null +++ b/src/components/SettingsPageComp/ClearUndo.tsx @@ -0,0 +1,44 @@ +import { useEffect, useState } from 'react'; + +type UndoPropsType = { + closeToast: () => void; + title: string; + btn: string; +}; + +const ClearUndo = (props: UndoPropsType) => { + const { closeToast, title, btn } = props; + const [deleteDataTimeout, setDeleteDataTimeout] = useState(null as unknown as NodeJS.Timeout); + + const dataClearance = () => { + localStorage.clear(); + }; + + useEffect(() => { + const timeoutId = setTimeout(() => { + dataClearance(); + }, 2000); + + setDeleteDataTimeout(timeoutId); + + return () => { + clearTimeout(timeoutId); + }; + }, []); + + const handleClick = () => { + clearTimeout(deleteDataTimeout); + closeToast(); + }; + + return ( +
    +

    {title}

    + +
    + ); +}; + +export default ClearUndo; diff --git a/src/components/SettingsPageComp/ConfirmModal.tsx b/src/components/SettingsPageComp/ConfirmModal.tsx new file mode 100644 index 0000000..faf5290 --- /dev/null +++ b/src/components/SettingsPageComp/ConfirmModal.tsx @@ -0,0 +1,45 @@ +import { Dispatch, FC, SetStateAction } from 'react'; + +import { toast } from 'react-toastify'; + +import FilledTonalButton from '@/shared/ui/FilledTonalButton'; +import Icon from '@/shared/ui/Icon'; +import TextButton from '@/shared/ui/TextButton'; + +import ClearUndo from './ClearUndo'; + +type PropsType = { + setIsShown: Dispatch>; + locales: { + title: string; + subtitle: string; + cancel: string; + confirm: string; + undoTitle: string; + cancelBtn: string; + }; +}; + +const ConfirmModal: FC = ({ setIsShown, locales }) => { + const { title, subtitle, cancel, confirm, undoTitle, cancelBtn } = locales; + return ( +
    + delete +

    {title}

    +

    {subtitle}

    +
    + setIsShown((prev) => !prev)}>{cancel} + { + toast( {}} title={undoTitle} btn={cancelBtn} />); + setIsShown((prev) => !prev); + }} + > + {confirm} + +
    +
    + ); +}; + +export default ConfirmModal; diff --git a/src/components/SettingsPageComp/ConfirmOverlay.tsx b/src/components/SettingsPageComp/ConfirmOverlay.tsx new file mode 100644 index 0000000..4f4ff58 --- /dev/null +++ b/src/components/SettingsPageComp/ConfirmOverlay.tsx @@ -0,0 +1,29 @@ +type PropsType = { + setIsShown: React.Dispatch>; + isShown: boolean; + children: JSX.Element; +}; + +const ConfirmOverlay = ({ isShown, setIsShown, children }: PropsType) => { + function closeHandler(e: React.MouseEvent | React.KeyboardEvent) { + if ((e.target as HTMLButtonElement).hasAttribute('data-overlay')) { + setIsShown((prev) => !prev); + } + } + if (!isShown) { + return null; + } + return ( +
    closeHandler(e)} + onKeyDown={(e) => closeHandler(e)} + className="overlay absolute left-0 top-0 z-10 flex h-full w-full items-center justify-center bg-black/60" + > + {children} +
    + ); +}; + +export default ConfirmOverlay; diff --git a/src/components/SettingsPageComp/DarkModeComp.tsx b/src/components/SettingsPageComp/DarkModeComp.tsx new file mode 100644 index 0000000..1031e9a --- /dev/null +++ b/src/components/SettingsPageComp/DarkModeComp.tsx @@ -0,0 +1,31 @@ +import { useState } from 'react'; + +import localStorageKeys from '@/shared/constants/localStorageKeys'; +import useLanguage from '@/shared/Context/hooks'; +import colorThemeSwitcher from '@/shared/helpers/colorThemeSwitcher'; +import Switch from '@/shared/ui/Switch'; + +const DarkModeComp = () => { + const { translation } = useLanguage(); + const [isDarkMode, setIsDarkMode] = useState(() => !localStorage.getItem(localStorageKeys.LIGHT_THEME)); + + function handleThemeSwitch() { + if (isDarkMode) { + colorThemeSwitcher.setLight(); + } else { + colorThemeSwitcher.setDark(); + } + setIsDarkMode((prev) => !prev); + } + return ( +
    +
    +

    {translation.settingsPage.mode.title}

    +

    {translation.settingsPage.mode.subtitle}

    +
    + handleThemeSwitch()} data-testid="themeSwitcher" /> +
    + ); +}; + +export default DarkModeComp; diff --git a/src/components/SettingsPageComp/EndpointComp.tsx b/src/components/SettingsPageComp/EndpointComp.tsx new file mode 100644 index 0000000..9506793 --- /dev/null +++ b/src/components/SettingsPageComp/EndpointComp.tsx @@ -0,0 +1,36 @@ +import { FC } from 'react'; + +import useLanguage from '@/shared/Context/hooks'; +import FilledTextField from '@/shared/ui/FilledTextField'; +import Icon from '@/shared/ui/Icon'; +import IconButton from '@/shared/ui/IconButton'; + +type PropsType = { + endpoint: string; + saveEndpoint: (value: string) => void; +}; + +const EndpointComp: FC = ({ endpoint, saveEndpoint }) => { + const { translation } = useLanguage(); + return ( +
    +
    +

    {translation.settingsPage.endpoint.title}

    +

    {translation.settingsPage.endpoint.subtitle}

    +
    + saveEndpoint((e?.target as HTMLInputElement).value)} + > + saveEndpoint('')}> + cancel + + +
    + ); +}; + +export default EndpointComp; diff --git a/src/components/SettingsPageComp/LangBtns.tsx b/src/components/SettingsPageComp/LangBtns.tsx new file mode 100644 index 0000000..2fc176c --- /dev/null +++ b/src/components/SettingsPageComp/LangBtns.tsx @@ -0,0 +1,44 @@ +import { FC } from 'react'; + +import cn from '@/shared/lib/helpers/cn'; +import Icon from '@/shared/ui/Icon'; + +type PropsType = { + language: string; + changeLanguage: (lang: 'en' | 'ru') => void; +}; + +const LangBtns: FC = ({ language, changeLanguage }) => { + const isEnglish = language === 'en'; + + return ( + <> + + + + ); +}; + +export default LangBtns; diff --git a/src/components/SettingsPageComp/LangSelectorComp.tsx b/src/components/SettingsPageComp/LangSelectorComp.tsx new file mode 100644 index 0000000..70be4d1 --- /dev/null +++ b/src/components/SettingsPageComp/LangSelectorComp.tsx @@ -0,0 +1,20 @@ +import useLanguage from '@/shared/Context/hooks'; + +import LangBtns from './LangBtns'; + +const LangSelectorComp = () => { + const { translation, language, changeLanguage } = useLanguage(); + return ( +
    +
    +

    {translation.settingsPage.lang.title}

    +

    {translation.settingsPage.lang.subtitle}

    +
    +
    + +
    +
    + ); +}; + +export default LangSelectorComp; diff --git a/src/components/SettingsPageComp/PersistHeadersComp.tsx b/src/components/SettingsPageComp/PersistHeadersComp.tsx new file mode 100644 index 0000000..f1fa91e --- /dev/null +++ b/src/components/SettingsPageComp/PersistHeadersComp.tsx @@ -0,0 +1,27 @@ +import { FC } from 'react'; + +import useLanguage from '@/shared/Context/hooks'; +import Switch from '@/shared/ui/Switch'; + +type PropsType = { + checked: boolean; + switcher: () => void; +}; + +const PersistHeaderComp: FC = ({ checked, switcher }) => { + const { translation } = useLanguage(); + return ( +
    +
    +

    {translation.settingsPage.headers.title}

    +
    +

    {translation.settingsPage.headers.firstSub}

    +

    {translation.settingsPage.headers.secondSub}

    +
    +
    + switcher()} /> +
    + ); +}; + +export default PersistHeaderComp; diff --git a/src/components/loginReg/FormInput.ts b/src/components/loginReg/FormInput.ts index ac1b076..6ff034b 100644 --- a/src/components/loginReg/FormInput.ts +++ b/src/components/loginReg/FormInput.ts @@ -7,6 +7,9 @@ const FormInput = createComponent({ react: React, tagName: 'md-outlined-text-field', elementClass: MdOutlinedTextField, + events: { + onChange: 'change', + }, }); export default FormInput; diff --git a/src/components/loginReg/PassVisibilityIcon.tsx b/src/components/loginReg/PassVisibilityIcon.tsx index 71d812e..1606e0a 100644 --- a/src/components/loginReg/PassVisibilityIcon.tsx +++ b/src/components/loginReg/PassVisibilityIcon.tsx @@ -18,7 +18,7 @@ const IconSlot = createComponent({ const PassVisibilityIcon = ({ onClick }: { onClick: () => void }) => { return ( - + visibility visibility_off diff --git a/src/layouts/MainLayout.tsx b/src/layouts/MainLayout.tsx index 406ab47..30acf3e 100644 --- a/src/layouts/MainLayout.tsx +++ b/src/layouts/MainLayout.tsx @@ -1,4 +1,5 @@ import { Outlet } from 'react-router-dom'; +import { ToastContainer } from 'react-toastify'; import Footer from '@components/Footer/Footer'; import Header from '@components/Header/Header'; @@ -16,6 +17,14 @@ const MainLayout = () => {
  • +
    diff --git a/src/locales/en.ts b/src/locales/en.ts index eb6eb15..293535d 100644 --- a/src/locales/en.ts +++ b/src/locales/en.ts @@ -18,8 +18,40 @@ const en = { linkClue: 'Already have an account?', linkTitle: 'Log in', }, - header: { h1: { h2: 'english' } }, - button: 'Press me', + settingsPage: { + headers: { + title: 'Persist headers', + firstSub: 'Save headers upon reloading.', + secondSub: 'Only enable if you trust this device.', + }, + mode: { + title: 'Dark mode', + subtitle: 'Adjust how the interface looks like.', + }, + endpoint: { + title: 'API endpoint', + subtitle: 'Change API endpoint.', + }, + lang: { + title: 'Language', + subtitle: 'Change app language.', + }, + clear: { + title: 'Clear storage', + subtitle: 'Remove all locally stored data.', + btn: 'Clear data', + modal: { + title: 'Clear data', + subtitle: 'Are you sure you want to clear all local storage data?', + cancel: 'Cancel', + confirm: 'Clear', + }, + undo: { + undoTitle: 'Local storage data cleared', + cancelBtn: 'Undo', + }, + }, + }, }; export default en; diff --git a/src/locales/ru.ts b/src/locales/ru.ts index 6ef84a1..68e7737 100644 --- a/src/locales/ru.ts +++ b/src/locales/ru.ts @@ -18,8 +18,40 @@ const ru = { linkClue: 'Уже есть аккаунт?', linkTitle: 'Войти', }, - header: { h1: { h2: 'русский' } }, - button: 'Нажми', + settingsPage: { + headers: { + title: 'Сохранять заголовки', + firstSub: 'Сохранять заголовки после перезагрузки.', + secondSub: 'Включайте, только если доверяете устройству.', + }, + mode: { + title: 'Тёмная тема', + subtitle: 'Изменить внешний вид интерфейса.', + }, + endpoint: { + title: 'API endpoint', + subtitle: 'Сменить API endpoint.', + }, + lang: { + title: 'Локализация', + subtitle: 'Сменить локализацию.', + }, + clear: { + title: 'Очистить хранилище', + subtitle: 'Очистить все локалько сохранённые данные.', + btn: 'Очистить', + modal: { + title: 'Очистить данные', + subtitle: 'Вы уверены, что хотити удалить все локальные данные?', + cancel: 'Отмена', + confirm: 'Очистить', + }, + undo: { + undoTitle: 'Данные хранилища очищены', + cancelBtn: 'Отменить', + }, + }, + }, }; export default ru; diff --git a/src/main.tsx b/src/main.tsx index 8f3d94a..a279fd0 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -8,6 +8,7 @@ import initFirebaseApp from './firebase'; import '@/styles/index.css'; import 'overlayscrollbars/overlayscrollbars.css'; +import 'react-toastify/dist/ReactToastify.css'; initFirebaseApp(); diff --git a/src/pages/LoginPage.tsx b/src/pages/LoginPage.tsx index 5076513..cb38014 100644 --- a/src/pages/LoginPage.tsx +++ b/src/pages/LoginPage.tsx @@ -5,6 +5,7 @@ import { TextFieldType } from '@material/web/textfield/outlined-text-field'; import { getAuth, signInWithEmailAndPassword } from 'firebase/auth'; import { useForm } from 'react-hook-form'; import { Link, useNavigate } from 'react-router-dom'; +import { toast } from 'react-toastify'; import PassVisibilityIcon from '@/components/loginReg/PassVisibilityIcon'; import AUTH_ERRORS from '@/shared/constants/authErrors'; @@ -14,7 +15,6 @@ import useAuth from '@/shared/Context/authHook'; import useLanguage from '@/shared/Context/hooks'; import notationLocalizer from '@/shared/helpers/notationLocalizer'; import switchPassType from '@/shared/helpers/switchPassType'; -import toastifyNotation from '@/shared/helpers/toastifyNotation'; import { ErrorType, TextInputProps } from '@/shared/types'; import FormInput from '@components/loginReg/FormInput'; import SubmitBtn from '@components/loginReg/SubmitBtn'; @@ -48,10 +48,10 @@ export default function LoginPage() { return null; } catch (e) { if ((e as ErrorType).code === AUTH_ERRORS.INVALID_EMAIL) - return toastifyNotation(notationLocalizer(language, 'code8')); + return toast(

    {notationLocalizer(language, 'code8')}

    ); if ((e as ErrorType).code === AUTH_ERRORS.INVALID_PASS) - return toastifyNotation(notationLocalizer(language, 'code9')); - return toastifyNotation(notationLocalizer(language, 'code11')); + return toast(

    {notationLocalizer(language, 'code9')}

    ); + return toast(

    {notationLocalizer(language, 'code11')}

    ); } } diff --git a/src/pages/SettingsPage.tsx b/src/pages/SettingsPage.tsx new file mode 100644 index 0000000..af781a9 --- /dev/null +++ b/src/pages/SettingsPage.tsx @@ -0,0 +1,34 @@ +import { useState } from 'react'; + +import ClearStorageComp from '@/components/SettingsPageComp/ClearStorageComp'; +import DarkModeComp from '@/components/SettingsPageComp/DarkModeComp'; +import EndpointComp from '@/components/SettingsPageComp/EndpointComp'; +import LangSelectorComp from '@/components/SettingsPageComp/LangSelectorComp'; +import PersistHeaderComp from '@components/SettingsPageComp/PersistHeadersComp'; + +const SettignsPage = () => { + const [settingsState, setSettingsState] = useState({ + headers: true, + endpoint: 'goods', + }); + + return ( +
    +
    + setSettingsState((prev) => ({ ...prev, headers: !prev.headers }))} + /> + + setSettingsState((prev) => ({ ...prev, endpoint: value }))} + /> + + +
    +
    + ); +}; + +export default SettignsPage; diff --git a/src/pages/SignUpPage.tsx b/src/pages/SignUpPage.tsx index d2067e1..a485345 100644 --- a/src/pages/SignUpPage.tsx +++ b/src/pages/SignUpPage.tsx @@ -5,6 +5,7 @@ import { TextFieldType } from '@material/web/textfield/outlined-text-field'; import { createUserWithEmailAndPassword, getAuth } from 'firebase/auth'; import { useForm } from 'react-hook-form'; import { Link, useNavigate } from 'react-router-dom'; +import { toast } from 'react-toastify'; import FormInput from '@/components/loginReg/FormInput'; import PassVisibilityIcon from '@/components/loginReg/PassVisibilityIcon'; @@ -16,7 +17,6 @@ import useAuth from '@/shared/Context/authHook'; import useLanguage from '@/shared/Context/hooks'; import notationLocalizer from '@/shared/helpers/notationLocalizer'; import switchPassType from '@/shared/helpers/switchPassType'; -import toastifyNotation from '@/shared/helpers/toastifyNotation'; import { ErrorType, TextInputProps } from '@/shared/types'; export default function SignUpPage() { @@ -49,8 +49,8 @@ export default function SignUpPage() { return null; } catch (e) { if ((e as ErrorType).code === AUTH_ERRORS.EMAIL_IN_USE) - return toastifyNotation(notationLocalizer(language, 'code10')); - return toastifyNotation(notationLocalizer(language, 'code11')); + return toast(

    {notationLocalizer(language, 'code10')}

    ); + return toast(

    {notationLocalizer(language, 'code11')}

    ); } } diff --git a/src/pages/WelcomePage.tsx b/src/pages/WelcomePage.tsx index f026e5f..954ba1d 100644 --- a/src/pages/WelcomePage.tsx +++ b/src/pages/WelcomePage.tsx @@ -4,11 +4,12 @@ import ROUTES from '@shared/constants/routes'; const WelcomePage = () => { return ( -
    +
    Here is my fancy welcome page!
    login main page + settings page
    ); diff --git a/src/router/router.tsx b/src/router/router.tsx index 65cc4c2..ed33f0a 100644 --- a/src/router/router.tsx +++ b/src/router/router.tsx @@ -2,11 +2,13 @@ import { createHashRouter } from 'react-router-dom'; import MainLayout from '@/layouts/MainLayout'; import LoginPage from '@/pages/LoginPage'; +import SettignsPage from '@/pages/SettingsPage'; import SignUpPage from '@/pages/SignUpPage'; import WelcomePage from '@/pages/WelcomePage'; import ROUTES from '@/shared/constants/routes'; import MainPage from '@pages/MainPage/MainPage'; +import AuthAllowedOnly from './AuthAllowedOnly'; import UnauthAllowedOnly from './UnauthAllowedOnly'; export const routes = [ @@ -55,6 +57,14 @@ export const routes = [ // ), }, + { + path: ROUTES.SETTINGS, + element: ( + + + + ), + }, ], }, ]; diff --git a/src/shared/Context/LanguageContext.tsx b/src/shared/Context/LanguageContext.tsx index af113eb..a1f9e94 100644 --- a/src/shared/Context/LanguageContext.tsx +++ b/src/shared/Context/LanguageContext.tsx @@ -7,7 +7,7 @@ type Translation = typeof enTranslation | typeof ruTranslation; type LanguageContextType = { language: string; - changeLanguage: () => void; + changeLanguage: (lang: 'en' | 'ru') => void; translation: Translation; }; @@ -21,10 +21,13 @@ export const LanguageContext = createContext({} as Language export default function LanguageProvider({ children }: { children: ReactNode }) { const [language, setLanguage] = useState('en'); - const changeLanguage = useCallback(() => { - const newLang = language === 'en' ? 'ru' : 'en'; - setLanguage(newLang); - }, [language]); + const changeLanguage = useCallback( + (lang: 'en' | 'ru') => { + if (lang === language) return; + setLanguage(lang); + }, + [language], + ); const translation = TranslationFiles[language]; const contextValue = useMemo( diff --git a/src/shared/constants/localStorageKeys.ts b/src/shared/constants/localStorageKeys.ts index 24565c6..06cad54 100644 --- a/src/shared/constants/localStorageKeys.ts +++ b/src/shared/constants/localStorageKeys.ts @@ -1,6 +1,7 @@ const localStorageKeys = { REQUEST_EDITOR_HEIGHT: 'request-height', RESPONSE_WIDTH: 'response-width', + LIGHT_THEME: 'graphiQlColorTheme', }; export default localStorageKeys; diff --git a/src/shared/constants/routes.ts b/src/shared/constants/routes.ts index 814c623..11edee9 100644 --- a/src/shared/constants/routes.ts +++ b/src/shared/constants/routes.ts @@ -4,6 +4,7 @@ const ROUTES = { LOGIN: 'login', SIGNUP: 'signup', MAIN: 'main', + SETTINGS: 'settings', } as const; export default ROUTES; diff --git a/src/shared/helpers/colorThemeSwitcher.ts b/src/shared/helpers/colorThemeSwitcher.ts new file mode 100644 index 0000000..a12e117 --- /dev/null +++ b/src/shared/helpers/colorThemeSwitcher.ts @@ -0,0 +1,12 @@ +const colorThemeSwitcher = { + setLight: () => { + document.body.setAttribute('data-user-theme', 'light'); + localStorage.setItem('graphiQlColorTheme', 'light'); + }, + setDark: () => { + document.body.removeAttribute('data-user-theme'); + localStorage.removeItem('graphiQlColorTheme'); + }, +}; + +export default colorThemeSwitcher; diff --git a/src/shared/helpers/toastifyNotation.ts b/src/shared/helpers/toastifyNotation.ts deleted file mode 100644 index 7487cc8..0000000 --- a/src/shared/helpers/toastifyNotation.ts +++ /dev/null @@ -1,12 +0,0 @@ -import Toastify from 'toastify-js'; -import 'toastify-js/src/toastify.css'; - -const toastifyNotation = (message: string) => { - return Toastify({ - text: message, - duration: 1500, - position: 'center', - }).showToast(); -}; - -export default toastifyNotation; diff --git a/src/shared/ui/FilledTextField.tsx b/src/shared/ui/FilledTextField.tsx new file mode 100644 index 0000000..a976c9a --- /dev/null +++ b/src/shared/ui/FilledTextField.tsx @@ -0,0 +1,15 @@ +import React from 'react'; + +import { createComponent } from '@lit/react'; +import { MdFilledTextField } from '@material/web/textfield/filled-text-field'; + +const FilledTextField = createComponent({ + react: React, + tagName: 'md-filled-text-field', + elementClass: MdFilledTextField, + events: { + onChange: 'change', + }, +}); + +export default FilledTextField; diff --git a/src/shared/ui/FilledTonalButton.tsx b/src/shared/ui/FilledTonalButton.tsx new file mode 100644 index 0000000..2c6f17e --- /dev/null +++ b/src/shared/ui/FilledTonalButton.tsx @@ -0,0 +1,12 @@ +import React from 'react'; + +import { createComponent } from '@lit/react'; +import { MdFilledTonalButton } from '@material/web/button/filled-tonal-button'; + +const FilledTonalButton = createComponent({ + react: React, + tagName: 'md-filled-tonal-button', + elementClass: MdFilledTonalButton, +}); + +export default FilledTonalButton; diff --git a/src/shared/ui/OutlinedButton.tsx b/src/shared/ui/OutlinedButton.tsx new file mode 100644 index 0000000..72ab5d3 --- /dev/null +++ b/src/shared/ui/OutlinedButton.tsx @@ -0,0 +1,12 @@ +import React from 'react'; + +import { createComponent } from '@lit/react'; +import { MdOutlinedButton } from '@material/web/button/outlined-button'; + +const OutlinedButton = createComponent({ + react: React, + tagName: 'md-outlined-button', + elementClass: MdOutlinedButton, +}); + +export default OutlinedButton; diff --git a/src/shared/ui/Switch.tsx b/src/shared/ui/Switch.tsx new file mode 100644 index 0000000..e0cce70 --- /dev/null +++ b/src/shared/ui/Switch.tsx @@ -0,0 +1,12 @@ +import React from 'react'; + +import { createComponent } from '@lit/react'; +import { MdSwitch } from '@material/web/switch/switch'; + +const Switch = createComponent({ + react: React, + tagName: 'md-switch', + elementClass: MdSwitch, +}); + +export default Switch; diff --git a/src/shared/ui/TextButton.tsx b/src/shared/ui/TextButton.tsx new file mode 100644 index 0000000..4c7bf93 --- /dev/null +++ b/src/shared/ui/TextButton.tsx @@ -0,0 +1,12 @@ +import React from 'react'; + +import { createComponent } from '@lit/react'; +import { MdTextButton } from '@material/web/button/text-button'; + +const TextButton = createComponent({ + react: React, + tagName: 'md-text-button', + elementClass: MdTextButton, +}); + +export default TextButton; diff --git a/src/styles/index.css b/src/styles/index.css index a013b8e..5b78703 100644 --- a/src/styles/index.css +++ b/src/styles/index.css @@ -1,14 +1,117 @@ @import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800&display=swap'); @import url('https://fonts.googleapis.com/css2?family=Readex+Pro:wght@200;300;400;500;600;700&display=swap'); @import url('https://fonts.googleapis.com/css2?family=Readex+Pro:wght@200;300;400;500;600;700&family=Roboto:wght@100;300;400;500;700;900&display=swap'); -@import 'theme.light.css' (prefers-color-scheme: light); -@import 'theme.dark.css' (prefers-color-scheme: dark); @import 'tokens.css'; @tailwind base; @tailwind components; @tailwind utilities; +:root { + --md-sys-color-primary: var(--md-sys-color-primary-dark); + --md-sys-color-on-primary: var(--md-sys-color-on-primary-dark); + --md-sys-color-primary-container: var(--md-sys-color-primary-container-dark); + --md-sys-color-on-primary-container: var(--md-sys-color-on-primary-container-dark); + --md-sys-color-primary-fixed: var(--md-sys-color-primary-fixed-dark); + --md-sys-color-on-primary-fixed: var(--md-sys-color-on-primary-fixed-dark); + --md-sys-color-primary-fixed-dim: var(--md-sys-color-primary-fixed-dim-dark); + --md-sys-color-on-primary-fixed-variant: var(--md-sys-color-on-primary-fixed-variant-dark); + --md-sys-color-secondary: var(--md-sys-color-secondary-dark); + --md-sys-color-on-secondary: var(--md-sys-color-on-secondary-dark); + --md-sys-color-secondary-container: var(--md-sys-color-secondary-container-dark); + --md-sys-color-on-secondary-container: var(--md-sys-color-on-secondary-container-dark); + --md-sys-color-secondary-fixed: var(--md-sys-color-secondary-fixed-dark); + --md-sys-color-on-secondary-fixed: var(--md-sys-color-on-secondary-fixed-dark); + --md-sys-color-secondary-fixed-dim: var(--md-sys-color-secondary-fixed-dim-dark); + --md-sys-color-on-secondary-fixed-variant: var(--md-sys-color-on-secondary-fixed-variant-dark); + --md-sys-color-tertiary: var(--md-sys-color-tertiary-dark); + --md-sys-color-on-tertiary: var(--md-sys-color-on-tertiary-dark); + --md-sys-color-tertiary-container: var(--md-sys-color-tertiary-container-dark); + --md-sys-color-on-tertiary-container: var(--md-sys-color-on-tertiary-container-dark); + --md-sys-color-tertiary-fixed: var(--md-sys-color-tertiary-fixed-dark); + --md-sys-color-on-tertiary-fixed: var(--md-sys-color-on-tertiary-fixed-dark); + --md-sys-color-tertiary-fixed-dim: var(--md-sys-color-tertiary-fixed-dim-dark); + --md-sys-color-on-tertiary-fixed-variant: var(--md-sys-color-on-tertiary-fixed-variant-dark); + --md-sys-color-error: var(--md-sys-color-error-dark); + --md-sys-color-on-error: var(--md-sys-color-on-error-dark); + --md-sys-color-error-container: var(--md-sys-color-error-container-dark); + --md-sys-color-on-error-container: var(--md-sys-color-on-error-container-dark); + --md-sys-color-outline: var(--md-sys-color-outline-dark); + --md-sys-color-background: var(--md-sys-color-background-dark); + --md-sys-color-on-background: var(--md-sys-color-on-background-dark); + --md-sys-color-surface: var(--md-sys-color-surface-dark); + --md-sys-color-on-surface: var(--md-sys-color-on-surface-dark); + --md-sys-color-surface-variant: var(--md-sys-color-surface-variant-dark); + --md-sys-color-on-surface-variant: var(--md-sys-color-on-surface-variant-dark); + --md-sys-color-inverse-surface: var(--md-sys-color-inverse-surface-dark); + --md-sys-color-inverse-on-surface: var(--md-sys-color-inverse-on-surface-dark); + --md-sys-color-inverse-primary: var(--md-sys-color-inverse-primary-dark); + --md-sys-color-shadow: var(--md-sys-color-shadow-dark); + --md-sys-color-surface-tint: var(--md-sys-color-surface-tint-dark); + --md-sys-color-outline-variant: var(--md-sys-color-outline-variant-dark); + --md-sys-color-scrim: var(--md-sys-color-scrim-dark); + --md-sys-color-surface-container-highest: var(--md-sys-color-surface-container-highest-dark); + --md-sys-color-surface-container-high: var(--md-sys-color-surface-container-high-dark); + --md-sys-color-surface-container: var(--md-sys-color-surface-container-dark); + --md-sys-color-surface-container-low: var(--md-sys-color-surface-container-low-dark); + --md-sys-color-surface-container-lowest: var(--md-sys-color-surface-container-lowest-dark); + --md-sys-color-surface-bright: var(--md-sys-color-surface-bright-dark); + --md-sys-color-surface-dim: var(--md-sys-color-surface-dim-dark); +} + +[data-user-theme="light"] { + --md-sys-color-primary: var(--md-sys-color-primary-light); + --md-sys-color-on-primary: var(--md-sys-color-on-primary-light); + --md-sys-color-primary-container: var(--md-sys-color-primary-container-light); + --md-sys-color-on-primary-container: var(--md-sys-color-on-primary-container-light); + --md-sys-color-primary-fixed: var(--md-sys-color-primary-fixed-light); + --md-sys-color-on-primary-fixed: var(--md-sys-color-on-primary-fixed-light); + --md-sys-color-primary-fixed-dim: var(--md-sys-color-primary-fixed-dim-light); + --md-sys-color-on-primary-fixed-variant: var(--md-sys-color-on-primary-fixed-variant-light); + --md-sys-color-secondary: var(--md-sys-color-secondary-light); + --md-sys-color-on-secondary: var(--md-sys-color-on-secondary-light); + --md-sys-color-secondary-container: var(--md-sys-color-secondary-container-light); + --md-sys-color-on-secondary-container: var(--md-sys-color-on-secondary-container-light); + --md-sys-color-secondary-fixed: var(--md-sys-color-secondary-fixed-light); + --md-sys-color-on-secondary-fixed: var(--md-sys-color-on-secondary-fixed-light); + --md-sys-color-secondary-fixed-dim: var(--md-sys-color-secondary-fixed-dim-light); + --md-sys-color-on-secondary-fixed-variant: var(--md-sys-color-on-secondary-fixed-variant-light); + --md-sys-color-tertiary: var(--md-sys-color-tertiary-light); + --md-sys-color-on-tertiary: var(--md-sys-color-on-tertiary-light); + --md-sys-color-tertiary-container: var(--md-sys-color-tertiary-container-light); + --md-sys-color-on-tertiary-container: var(--md-sys-color-on-tertiary-container-light); + --md-sys-color-tertiary-fixed: var(--md-sys-color-tertiary-fixed-light); + --md-sys-color-on-tertiary-fixed: var(--md-sys-color-on-tertiary-fixed-light); + --md-sys-color-tertiary-fixed-dim: var(--md-sys-color-tertiary-fixed-dim-light); + --md-sys-color-on-tertiary-fixed-variant: var(--md-sys-color-on-tertiary-fixed-variant-light); + --md-sys-color-error: var(--md-sys-color-error-light); + --md-sys-color-on-error: var(--md-sys-color-on-error-light); + --md-sys-color-error-container: var(--md-sys-color-error-container-light); + --md-sys-color-on-error-container: var(--md-sys-color-on-error-container-light); + --md-sys-color-outline: var(--md-sys-color-outline-light); + --md-sys-color-background: var(--md-sys-color-background-light); + --md-sys-color-on-background: var(--md-sys-color-on-background-light); + --md-sys-color-surface: var(--md-sys-color-surface-light); + --md-sys-color-on-surface: var(--md-sys-color-on-surface-light); + --md-sys-color-surface-variant: var(--md-sys-color-surface-variant-light); + --md-sys-color-on-surface-variant: var(--md-sys-color-on-surface-variant-light); + --md-sys-color-inverse-surface: var(--md-sys-color-inverse-surface-light); + --md-sys-color-inverse-on-surface: var(--md-sys-color-inverse-on-surface-light); + --md-sys-color-inverse-primary: var(--md-sys-color-inverse-primary-light); + --md-sys-color-shadow: var(--md-sys-color-shadow-light); + --md-sys-color-surface-tint: var(--md-sys-color-surface-tint-light); + --md-sys-color-outline-variant: var(--md-sys-color-outline-variant-light); + --md-sys-color-scrim: var(--md-sys-color-scrim-light); + --md-sys-color-surface-container-highest: var(--md-sys-color-surface-container-highest-light); + --md-sys-color-surface-container-high: var(--md-sys-color-surface-container-high-light); + --md-sys-color-surface-container: var(--md-sys-color-surface-container-light); + --md-sys-color-surface-container-low: var(--md-sys-color-surface-container-low-light); + --md-sys-color-surface-container-lowest: var(--md-sys-color-surface-container-lowest-light); + --md-sys-color-surface-bright: var(--md-sys-color-surface-bright-light); + --md-sys-color-surface-dim: var(--md-sys-color-surface-dim-light); + +} + @layer base { :root { color-scheme: dark; @@ -265,3 +368,11 @@ text-transform: var(--md-sys-typescale-title-small-text-transform); text-decoration: var(--md-sys-typescale-title-small-text-decoration); } + +[data-clear-btn] { + --md-filled-tonal-button-label-text-color: var(--md-sys-color-primary) +} + +.toasttest { + background-color: red; +} diff --git a/src/test/SettingsPage/SettingsPage.test.tsx b/src/test/SettingsPage/SettingsPage.test.tsx new file mode 100644 index 0000000..88024ff --- /dev/null +++ b/src/test/SettingsPage/SettingsPage.test.tsx @@ -0,0 +1,83 @@ +import { act, fireEvent, render, screen, waitForElementToBeRemoved } from '@testing-library/react'; +import { describe, expect, it } from 'vitest'; + +import App from '@/app/App'; +import { prepareAuthCookie } from '@/shared/helpers/cookieHandlers'; + +document.cookie = prepareAuthCookie('test@gmail.com'); + +describe('Testing for settings page', () => { + it('Should render settigns page properly', async () => { + render(); + expect(screen.queryByText('Persist headers')).toBeNull(); + expect(screen.queryByText('Dark mode')).toBeNull(); + expect(screen.queryByText('Clear storage')).toBeNull(); + const settingsLink = await screen.findByText('settings page'); + await act(async () => { + fireEvent.click(settingsLink); + }); + expect(await screen.findByText('Persist headers')).toBeInTheDocument(); + expect(await screen.findByText('Dark mode')).toBeInTheDocument(); + expect(await screen.findByText('Clear storage')).toBeInTheDocument(); + }); + it('Should change app language interface after clicking on russian language button and back', async () => { + render(); + const RuslangBtn = await screen.findByText('Русский'); + const EngLangBtn = await screen.findByText('English'); + await act(async () => { + fireEvent.click(RuslangBtn); + }); + expect(await screen.findByText('Сохранять заголовки')).toBeInTheDocument(); + expect(await screen.findByText('Тёмная тема')).toBeInTheDocument(); + expect(await screen.findByText('Очистить хранилище')).toBeInTheDocument(); + await act(async () => { + fireEvent.click(EngLangBtn); + }); + expect(await screen.findByText('Persist headers')).toBeInTheDocument(); + expect(await screen.findByText('Dark mode')).toBeInTheDocument(); + expect(await screen.findByText('Clear storage')).toBeInTheDocument(); + }); + it('Should opened modal and close it with proper buttons', async () => { + render(); + const clearDataBtn = screen.getByText('Clear data'); + expect(screen.queryByTestId('overlay')).toBeNull(); + await act(async () => { + fireEvent.click(clearDataBtn); + }); + expect(await screen.findByTestId('overlay')).toBeInTheDocument(); + const closeBtn = await screen.findByText('Cancel'); + await act(async () => { + fireEvent.click(closeBtn); + }); + waitForElementToBeRemoved(() => { + expect(screen.queryByTestId('overlay')).toBeNull(); + }).catch(() => {}); + }); + it('Should open confirm modal and close it after clicking corresponding buttons', async () => { + render(); + const clearDataBtn = screen.getByText('Clear data'); + await act(async () => { + fireEvent.click(clearDataBtn); + }); + const clearBtn = await screen.findByText('Clear'); + await act(async () => { + fireEvent.click(clearBtn); + }); + const undoBtn = await screen.findByText('Undo'); + await act(async () => { + fireEvent.click(undoBtn); + }); + waitForElementToBeRemoved(() => { + expect(undoBtn).toBeNull(); + }).catch(() => {}); + }); + it('Should change color theme after clicking on theme switcher', async () => { + render(); + expect(document.body.getAttribute('data-user-theme')).toBeNull(); + const switcher = screen.getByTestId('themeSwitcher'); + await act(async () => { + fireEvent.click(switcher); + }); + expect(document.body.getAttribute('data-user-theme')).toMatch('light'); + }); +}); diff --git a/src/test/docsComponent/SomeCompletelyDiffFile.test.tsx b/src/test/docsComponent/DocsComp.test.tsx similarity index 99% rename from src/test/docsComponent/SomeCompletelyDiffFile.test.tsx rename to src/test/docsComponent/DocsComp.test.tsx index 8acd14b..a6287cb 100644 --- a/src/test/docsComponent/SomeCompletelyDiffFile.test.tsx +++ b/src/test/docsComponent/DocsComp.test.tsx @@ -132,7 +132,4 @@ describe('Testing for docs component', () => { await screen.findByText('A GraphQL schema provides a root type for each kind of operation.'), ).toBeInTheDocument(); }); - it('fake', () => { - expect(1).toBe(1); - }); }); diff --git a/src/test/helpers/RenderWithRouter.tsx b/src/test/helpers/RenderWithRouter.tsx index 04853bb..adbcc84 100644 --- a/src/test/helpers/RenderWithRouter.tsx +++ b/src/test/helpers/RenderWithRouter.tsx @@ -18,7 +18,7 @@ function renderWithRouter(element?: ReactNode | null, initialEntries?: string[], render( - ) + , ); diff --git a/src/test/toastifyNotation.test.tsx b/src/test/toastifyNotation.test.tsx deleted file mode 100644 index 60c467c..0000000 --- a/src/test/toastifyNotation.test.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import { render } from '@testing-library/react'; -import { describe, expect, it } from 'vitest'; - -import App from '@/app/App'; -import toastifyNotation from '@/shared/helpers/toastifyNotation'; - -describe('Testing for toastify notaions', () => { - it('Should display toastify alert with proper message', async () => { - render(); - toastifyNotation('Test msg'); - expect((document.body.firstChild as HTMLDivElement).innerText).toMatch('Test msg'); - }); -});