diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index aef8470da..3bfea65ab 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -31,7 +31,7 @@ jobs: - name: Check for lint issues run: | - yarn lint && yarn prettier . --check + yarn lint && yarn prettier . --check --log-level warn - name: Check for TS errors run: | diff --git a/.prettierignore b/.prettierignore index 6fbef3369..e0d67a665 100644 --- a/.prettierignore +++ b/.prettierignore @@ -11,6 +11,9 @@ node_modules/ coverage/ .nx/ .turbo/ +integration-tests/node_modules/ +integration-tests/playwright-report/ +integration-tests/test-results/ # Public compiled assets public/js/ diff --git a/eslint.config.mjs b/eslint.config.mjs index f8eeb92da..741110c52 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -262,6 +262,19 @@ export default [ rules: { // unit test usually need to mock before importing to make the mocking work 'import/first': 'off', + + // 🚫 forbid describe.only, it.only, test.only, etc. + 'no-restricted-syntax': [ + 'warn', + { + selector: + 'CallExpression[callee.object.name="describe"][callee.property.name="only"], ' + + 'CallExpression[callee.object.name="it"][callee.property.name="only"], ' + + 'CallExpression[callee.object.name="$it"][callee.property.name="only"], ' + + 'CallExpression[callee.object.name="test"][callee.property.name="only"]', + message: 'Remove .only from tests before committing.', + }, + ], }, }, ]; diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 6475324b0..d085b37f2 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,9 +1,7 @@ import { BrowserRouter as Router, Route, Routes } from 'react-router-dom'; import './css/App.css'; import './css/index.css'; -import { CssBaseline } from '@mui/material'; -import { CustomThemeProvider } from '@Context/Theme/themeContext'; -import Room from './pages/MeetingRoom/index'; +import MeetingRoom from './pages/MeetingRoom'; import GoodBye from './pages/GoodBye/index'; import WaitingRoom from './pages/WaitingRoom'; import SessionProvider from './Context/SessionProvider/session'; @@ -13,11 +11,24 @@ import { PublisherProvider } from './Context/PublisherProvider'; import RedirectToWaitingRoom from './components/RedirectToWaitingRoom'; import UnsupportedBrowserPage from './pages/UnsupportedBrowserPage'; import RoomContext from './Context/RoomContext'; +import Box from '@ui/Box'; +import useTheme, { ThemeProvider } from '@ui/theme'; const App = () => { + const theme = useTheme(); + return ( - - + }> @@ -35,7 +46,7 @@ const App = () => { - + @@ -47,8 +58,16 @@ const App = () => { } /> - + + ); +}; + +const AppWrapper = () => { + return ( + + + ); }; -export default App; +export default AppWrapper; diff --git a/frontend/src/Context/BackgroundEffectsDialog/BackgroundEffectsDialogContext.tsx b/frontend/src/Context/BackgroundEffectsDialog/BackgroundEffectsDialogContext.tsx new file mode 100644 index 000000000..3cdde4a89 --- /dev/null +++ b/frontend/src/Context/BackgroundEffectsDialog/BackgroundEffectsDialogContext.tsx @@ -0,0 +1,27 @@ +import createContext from 'react-global-state-hooks/createContext'; + +/** + * BackgroundEffectsDialog Context + * Manages the state of the Background Effects Dialog. + */ +const backgroundEffectsDialog$ = createContext( + { + isOpen: false, + }, + { + actions: { + open() { + return ({ setState }) => { + setState((state) => ({ ...state, isOpen: true })); + }; + }, + close() { + return ({ setState }) => { + setState((state) => ({ ...state, isOpen: false })); + }; + }, + }, + } +); + +export default backgroundEffectsDialog$; diff --git a/frontend/src/Context/BackgroundEffectsDialog/index.ts b/frontend/src/Context/BackgroundEffectsDialog/index.ts new file mode 100644 index 000000000..9800299f7 --- /dev/null +++ b/frontend/src/Context/BackgroundEffectsDialog/index.ts @@ -0,0 +1 @@ +export { default } from './BackgroundEffectsDialogContext'; diff --git a/frontend/src/Context/Theme/helpers/isDarkMode.ts b/frontend/src/Context/Theme/helpers/isDarkMode.ts deleted file mode 100644 index b5404ebee..000000000 --- a/frontend/src/Context/Theme/helpers/isDarkMode.ts +++ /dev/null @@ -1,5 +0,0 @@ -function isDarkMode(): boolean { - return globalThis.matchMedia && globalThis.matchMedia('(prefers-color-scheme: dark)').matches; -} - -export default isDarkMode; diff --git a/frontend/src/Context/Theme/index.ts b/frontend/src/Context/Theme/index.ts deleted file mode 100644 index 5d74ed154..000000000 --- a/frontend/src/Context/Theme/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default } from './hooks/useCustomTheme'; diff --git a/frontend/src/Context/Theme/themeContext.tsx b/frontend/src/Context/Theme/themeContext.tsx deleted file mode 100644 index f082dddf7..000000000 --- a/frontend/src/Context/Theme/themeContext.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { ThemeProvider } from '@mui/material'; -import React, { PropsWithChildren, useState } from 'react'; -import getTokensByMode, { ThemeTokens } from './helpers/getTokensByMode'; -import isDarkMode from './helpers/isDarkMode'; -import useSynchronizeThemeAndMedia from './hooks/useSynchronizeThemeAndMedia'; -import getMuiCustomTheme from './helpers/getMuiCustomTheme'; - -const themeContext = React.createContext(getTokensByMode('light')); - -export const CustomThemeProvider: React.FC = ({ children }) => { - const [tokens, setTokens] = useState(() => { - return isDarkMode() ? getTokensByMode('dark') : getTokensByMode('light'); - }); - - const theme = getMuiCustomTheme({ tokens }); - - useSynchronizeThemeAndMedia({ setTokens }); - - return ( - - {children} - - ); -}; - -export default themeContext; diff --git a/frontend/src/components/BackgroundEffects/AddBackgroundEffect/AddBackgroundEffectLayout/AddBackgroundEffectLayout.spec.tsx b/frontend/src/components/BackgroundEffects/AddBackgroundEffect/AddBackgroundEffectLayout/AddBackgroundEffectLayout.spec.tsx index 8d309974e..cd589c1a0 100644 --- a/frontend/src/components/BackgroundEffects/AddBackgroundEffect/AddBackgroundEffectLayout/AddBackgroundEffectLayout.spec.tsx +++ b/frontend/src/components/BackgroundEffects/AddBackgroundEffect/AddBackgroundEffectLayout/AddBackgroundEffectLayout.spec.tsx @@ -27,7 +27,7 @@ vi.mock('react-i18next', () => ({ }), })); -vi.mock('../../../../utils/useImageStorage/useImageStorage', () => ({ +vi.mock('@utils/useImageStorage/useImageStorage', () => ({ __esModule: true, default: () => ({ storageError: '', diff --git a/frontend/src/components/BackgroundEffects/AddBackgroundEffect/AddBackgroundEffectLayout/AddBackgroundEffectLayout.tsx b/frontend/src/components/BackgroundEffects/AddBackgroundEffect/AddBackgroundEffectLayout/AddBackgroundEffectLayout.tsx index 1cde720d0..2472cb899 100644 --- a/frontend/src/components/BackgroundEffects/AddBackgroundEffect/AddBackgroundEffectLayout/AddBackgroundEffectLayout.tsx +++ b/frontend/src/components/BackgroundEffects/AddBackgroundEffect/AddBackgroundEffectLayout/AddBackgroundEffectLayout.tsx @@ -12,7 +12,7 @@ import LinkIcon from '@mui/icons-material/Link'; import { useTranslation } from 'react-i18next'; import FileUploader from '../../FileUploader/FileUploader'; import { ALLOWED_TYPES, MAX_SIZE_MB } from '../../../../utils/constants'; -import useImageStorage from '../../../../utils/useImageStorage/useImageStorage'; +import useImageStorage from '@utils/useImageStorage/useImageStorage'; export type AddBackgroundEffectLayoutProps = { customBackgroundImageChange: (dataUrl: string) => void; diff --git a/frontend/src/components/BackgroundEffects/BackgroundGallery/BackgroundGallery.spec.tsx b/frontend/src/components/BackgroundEffects/BackgroundGallery/BackgroundGallery.spec.tsx index 1e0385bd1..36ef2128d 100644 --- a/frontend/src/components/BackgroundEffects/BackgroundGallery/BackgroundGallery.spec.tsx +++ b/frontend/src/components/BackgroundEffects/BackgroundGallery/BackgroundGallery.spec.tsx @@ -27,7 +27,7 @@ const backgrounds = [ const mockDeleteImageFromStorage = vi.fn(); const mockGetImagesFromStorage = vi.fn(() => customImages); -vi.mock('../../../utils/useImageStorage/useImageStorage', () => ({ +vi.mock('@utils/useImageStorage/useImageStorage', () => ({ __esModule: true, default: () => ({ getImagesFromStorage: mockGetImagesFromStorage, diff --git a/frontend/src/components/BackgroundEffects/BackgroundGallery/BackgroundGallery.tsx b/frontend/src/components/BackgroundEffects/BackgroundGallery/BackgroundGallery.tsx index 9f0adfd15..f77809177 100644 --- a/frontend/src/components/BackgroundEffects/BackgroundGallery/BackgroundGallery.tsx +++ b/frontend/src/components/BackgroundEffects/BackgroundGallery/BackgroundGallery.tsx @@ -4,7 +4,7 @@ import DeleteIcon from '@mui/icons-material/Delete'; import { useTranslation } from 'react-i18next'; import { BACKGROUNDS_PATH } from '../../../utils/constants'; import SelectableOption from '../SelectableOption'; -import useImageStorage, { StoredImage } from '../../../utils/useImageStorage/useImageStorage'; +import useImageStorage, { StoredImage } from '@utils/useImageStorage/useImageStorage'; export type BackgroundGalleryProps = { backgroundSelected: string; diff --git a/frontend/src/components/BackgroundEffects/SelectableOption/SelectableOption.tsx b/frontend/src/components/BackgroundEffects/SelectableOption/SelectableOption.tsx index 13e65348d..1f276719f 100644 --- a/frontend/src/components/BackgroundEffects/SelectableOption/SelectableOption.tsx +++ b/frontend/src/components/BackgroundEffects/SelectableOption/SelectableOption.tsx @@ -1,6 +1,6 @@ import { ReactElement, ReactNode } from 'react'; import { Box, Paper, Tooltip } from '@mui/material'; -import useCustomTheme from '@Context/Theme'; +import useTheme from '@ui/theme'; import { DEFAULT_SELECTABLE_OPTION_WIDTH } from '../../../utils/constants'; export type SelectableOptionProps = { @@ -43,7 +43,7 @@ const SelectableOption = ({ children, ...otherProps // Used by MUI Tooltip }: SelectableOptionProps): ReactElement => { - const theme = useCustomTheme(); + const theme = useTheme(); return ( { - const theme = useCustomTheme(); + const theme = useTheme(); return ( -
- +
+ - - + - - + />
); diff --git a/frontend/src/components/BannerLanguage/BannerLanguage.tsx b/frontend/src/components/BannerLanguage/BannerLanguage.tsx index 300d6f17a..96892d4a9 100644 --- a/frontend/src/components/BannerLanguage/BannerLanguage.tsx +++ b/frontend/src/components/BannerLanguage/BannerLanguage.tsx @@ -1,6 +1,8 @@ -import { ReactElement } from 'react'; import Box from '@ui/Box'; import LanguageSelector from '../LanguageSelector'; +import type { BoxProps } from '@mui/material'; + +type BannerLanguageProps = BoxProps; /** * BannerLanguage Component @@ -8,9 +10,9 @@ import LanguageSelector from '../LanguageSelector'; * This component provides a language selection feature for the banner. * @returns {ReactElement} The BannerLanguage component. */ -const BannerLanguage = (): ReactElement => { +const BannerLanguage: React.FC = (props) => { return ( - + ); diff --git a/frontend/src/components/BannerLogo/BannerLogo.tsx b/frontend/src/components/BannerLogo/BannerLogo.tsx index b081a16fa..d8d631190 100644 --- a/frontend/src/components/BannerLogo/BannerLogo.tsx +++ b/frontend/src/components/BannerLogo/BannerLogo.tsx @@ -17,15 +17,7 @@ const BannerLogo = (): ReactElement => { }; return ( - + { - const theme = useCustomTheme(); + const theme = useTheme(); return ( { const { t } = useTranslation(); - const theme = useCustomTheme(); + const theme = useTheme(); return ( diff --git a/frontend/src/components/GoodBye/GoodbyeMessage/GoodbyeMessage.tsx b/frontend/src/components/GoodBye/GoodbyeMessage/GoodbyeMessage.tsx index f16d6b827..806218e76 100644 --- a/frontend/src/components/GoodBye/GoodbyeMessage/GoodbyeMessage.tsx +++ b/frontend/src/components/GoodBye/GoodbyeMessage/GoodbyeMessage.tsx @@ -27,7 +27,7 @@ const GoodByeMessage = ({ header, message, roomName }: GoodByeMessageProps): Rea navigate(`/waiting-room/${roomName}`); }; return ( -
+

{header}

diff --git a/frontend/src/components/JoinContainerSeparator/JoinContainerSeparator.tsx b/frontend/src/components/JoinContainerSeparator/JoinContainerSeparator.tsx index 1ea1d194a..d7b723cac 100644 --- a/frontend/src/components/JoinContainerSeparator/JoinContainerSeparator.tsx +++ b/frontend/src/components/JoinContainerSeparator/JoinContainerSeparator.tsx @@ -2,7 +2,7 @@ import { ReactElement } from 'react'; import { useTranslation } from 'react-i18next'; import Stack from '@ui/Stack'; import Typography from '@ui/Typography'; -import useCustomTheme from '@Context/Theme'; +import useTheme from '@ui/theme'; import Separator from '../Separator'; /** @@ -13,7 +13,7 @@ import Separator from '../Separator'; */ const JoinContainerSeparator = (): ReactElement => { const { t } = useTranslation(); - const theme = useCustomTheme(); + const theme = useTheme(); return ( diff --git a/frontend/src/components/JoinExistingRoom/JoinExistingRoom.tsx b/frontend/src/components/JoinExistingRoom/JoinExistingRoom.tsx index ec89eeca8..fdab97009 100644 --- a/frontend/src/components/JoinExistingRoom/JoinExistingRoom.tsx +++ b/frontend/src/components/JoinExistingRoom/JoinExistingRoom.tsx @@ -14,13 +14,7 @@ const JoinExistingRoom = (): ReactElement => { const [hasError, setHasError] = useState(false); return ( - + { const { t } = useTranslation(); - const theme = useCustomTheme(); + const theme = useTheme(); const primaryWord = t('landing.primary.word'); const renderTitle = (titleKey: string) => { @@ -24,7 +24,6 @@ const LandingPageWelcome = (): ReactElement => { variant="h1" sx={{ color: isPrimaryWord ? theme.colors.textPrimary : theme.colors.textSecondary, - mr: { xs: 1, md: 0 }, }} > {text} @@ -36,10 +35,10 @@ const LandingPageWelcome = (): ReactElement => { { flexWrap: 'wrap', flexDirection: { xs: 'row', md: 'column' }, width: 'fit-content', + gap: 1, }} > <> @@ -63,7 +63,6 @@ const LandingPageWelcome = (): ReactElement => { variant="h4" sx={{ color: theme.colors.textTertiary, - mt: 1, display: { xs: 'none', sm: 'block', diff --git a/frontend/src/components/LanguageSelector/LanguageSelector.tsx b/frontend/src/components/LanguageSelector/LanguageSelector.tsx index 201860923..9fa3a8cb3 100644 --- a/frontend/src/components/LanguageSelector/LanguageSelector.tsx +++ b/frontend/src/components/LanguageSelector/LanguageSelector.tsx @@ -5,7 +5,8 @@ import MenuItem from '@ui/MenuItem'; import FormControl from '@ui/FormControl'; import Select from '@ui/Select'; import { SelectChangeEvent } from '@ui/SelectChangeEvent'; -import useCustomTheme from '@Context/Theme'; +import useTheme from '@ui/theme'; +import { SvgIconProps } from '@mui/material'; import { LanguageOption, LanguageSelectorProps } from './LanguageSelector.types'; import useIsSmallViewport from '../../hooks/useIsSmallViewport'; import VividIcon from '../VividIcon/VividIcon'; @@ -19,6 +20,10 @@ const languageOptions: LanguageOption[] = [ { code: 'es-MX', name: 'Español (México)', flag: 'flag-mexico' }, ]; +const ChevronDownIcon = (props: SvgIconProps) => ( + +); + /** * LanguageSelector Component * A dropdown component that allows users to select their preferred language. @@ -29,7 +34,7 @@ const languageOptions: LanguageOption[] = [ */ const LanguageSelector = ({ showFlag = true }: LanguageSelectorProps): ReactElement => { const { i18n } = useTranslation(); - const theme = useCustomTheme(); + const theme = useTheme(); const isSmallViewport = useIsSmallViewport(); const supportedLanguages = languageOptions.filter((option) => @@ -48,6 +53,7 @@ const LanguageSelector = ({ showFlag = true }: LanguageSelectorProps): ReactElem