diff --git a/frontend/src/Context/Theme/helpers/getMuiCustomTheme.ts b/frontend/src/Context/Theme/helpers/getMuiCustomTheme.ts
index 3e6461091..d853a5cd0 100644
--- a/frontend/src/Context/Theme/helpers/getMuiCustomTheme.ts
+++ b/frontend/src/Context/Theme/helpers/getMuiCustomTheme.ts
@@ -154,6 +154,16 @@ const getMuiCustomTheme = ({ tokens }: { tokens: ThemeTokens }) => {
},
},
},
+ MuiList: {
+ styleOverrides: {
+ root: {
+ backgroundColor: colors.onSecondary,
+ color: colors.textSecondary,
+ fontSize: tokens.typography.typeScale.desktop['body-base'].fontSize.value,
+ lineHeight: tokens.typography.typeScale.desktop['body-base'].lineHeight.value,
+ },
+ },
+ },
MuiTypography: {
styleOverrides: {
h1: createResponsiveTypography(tokens, 'headline', 'headline'),
diff --git a/frontend/src/components/Banner/Banner.spec.tsx b/frontend/src/components/Banner/Banner.spec.tsx
index f484997cc..7e34590df 100644
--- a/frontend/src/components/Banner/Banner.spec.tsx
+++ b/frontend/src/components/Banner/Banner.spec.tsx
@@ -14,26 +14,4 @@ describe('Banner', () => {
const bannerLogo = screen.getByTestId('banner-logo');
expect(bannerLogo).toBeInTheDocument();
});
-
- it('renders the banner date and time', () => {
- render(
-
-
-
- );
-
- const bannerDataTime = screen.getByTestId('dateAndTime');
- expect(bannerDataTime).toBeInTheDocument();
- });
-
- it('renders the banner links', () => {
- render(
-
-
-
- );
-
- const bannerLinks = screen.getByTestId('banner-links');
- expect(bannerLinks).toBeInTheDocument();
- });
});
diff --git a/frontend/src/components/Banner/Banner.tsx b/frontend/src/components/Banner/Banner.tsx
index 6d051dcd8..1de09712f 100644
--- a/frontend/src/components/Banner/Banner.tsx
+++ b/frontend/src/components/Banner/Banner.tsx
@@ -1,10 +1,8 @@
import { ReactElement } from 'react';
-import { AppBar, Toolbar } from '@mui/material';
import Box from '@ui/Box';
import Stack from '@ui/Stack';
import useCustomTheme from '@Context/Theme';
-import BannerDateTime from '../BannerDateTime';
-import BannerLinks from '../BannerLinks';
+import Header from '@ui/Header';
import BannerLogo from '../BannerLogo';
import BannerLanguage from '../BannerLanguage';
@@ -18,30 +16,27 @@ const Banner = (): ReactElement => {
const theme = useCustomTheme();
return (
-
-
-
-
-
+
-
+
+
+
+
+
+
);
};
diff --git a/frontend/src/components/BannerDateTime/BannerDateTime.spec.tsx b/frontend/src/components/BannerDateTime/BannerDateTime.spec.tsx
deleted file mode 100644
index ff31edbfb..000000000
--- a/frontend/src/components/BannerDateTime/BannerDateTime.spec.tsx
+++ /dev/null
@@ -1,17 +0,0 @@
-import { describe, expect, it, vi } from 'vitest';
-import { render, screen } from '@testing-library/react';
-import BannerDateTime from './BannerDateTime';
-
-describe('BannerDateTime', () => {
- it('renders the banner date and time', () => {
- const rawDate = new Date('June 27, 2024 21:12:00');
- vi.setSystemTime(rawDate);
- render();
-
- const bannerTime = screen.getByTestId('current-time');
- expect(bannerTime.textContent).toBe('9:12 PM');
-
- const bannerDate = screen.getByTestId('current-date');
- expect(bannerDate.textContent).toBe('Thu, Jun 27');
- });
-});
diff --git a/frontend/src/components/BannerDateTime/BannerDateTime.tsx b/frontend/src/components/BannerDateTime/BannerDateTime.tsx
deleted file mode 100644
index 7896c1b41..000000000
--- a/frontend/src/components/BannerDateTime/BannerDateTime.tsx
+++ /dev/null
@@ -1,29 +0,0 @@
-import { ReactElement } from 'react';
-import useDateTime from '../../hooks/useDateTime';
-
-/**
- * BannerDateTime Component
- *
- * This component returns a UI that includes current time and date.
- * @returns {ReactElement} - the banner with a date and time component
- */
-const BannerDateTime = (): ReactElement => {
- const { date, time } = useDateTime();
-
- return (
-
-
- {time}
-
- •
-
- {date}
-
-
- );
-};
-
-export default BannerDateTime;
diff --git a/frontend/src/components/BannerDateTime/index.tsx b/frontend/src/components/BannerDateTime/index.tsx
deleted file mode 100644
index b02695fd0..000000000
--- a/frontend/src/components/BannerDateTime/index.tsx
+++ /dev/null
@@ -1,3 +0,0 @@
-import BannerDateTime from './BannerDateTime';
-
-export default BannerDateTime;
diff --git a/frontend/src/components/BannerLanguage/BannerLanguage.tsx b/frontend/src/components/BannerLanguage/BannerLanguage.tsx
index b77dedd6a..300d6f17a 100644
--- a/frontend/src/components/BannerLanguage/BannerLanguage.tsx
+++ b/frontend/src/components/BannerLanguage/BannerLanguage.tsx
@@ -1,4 +1,5 @@
import { ReactElement } from 'react';
+import Box from '@ui/Box';
import LanguageSelector from '../LanguageSelector';
/**
@@ -9,9 +10,9 @@ import LanguageSelector from '../LanguageSelector';
*/
const BannerLanguage = (): ReactElement => {
return (
-
+
-
+
);
};
diff --git a/frontend/src/components/BannerLinks/BannerLinks.spec.tsx b/frontend/src/components/BannerLinks/BannerLinks.spec.tsx
deleted file mode 100644
index 8f0dd9279..000000000
--- a/frontend/src/components/BannerLinks/BannerLinks.spec.tsx
+++ /dev/null
@@ -1,17 +0,0 @@
-import { describe, expect, it } from 'vitest';
-import { render, screen } from '@testing-library/react';
-import { MemoryRouter } from 'react-router-dom';
-import BannerLinks from './BannerLinks';
-
-describe('BannerLinks', () => {
- it('renders the banner links component', () => {
- render(
-
-
-
- );
-
- const bannerLinks = screen.getByTestId('banner-links');
- expect(bannerLinks).toBeInTheDocument();
- });
-});
diff --git a/frontend/src/components/BannerLinks/BannerLinks.tsx b/frontend/src/components/BannerLinks/BannerLinks.tsx
deleted file mode 100644
index 20a04aa4a..000000000
--- a/frontend/src/components/BannerLinks/BannerLinks.tsx
+++ /dev/null
@@ -1,18 +0,0 @@
-import { ReactElement } from 'react';
-import GHRepoButton from '../GHRepoButton';
-
-/**
- * BannerLinks Component
- *
- * Component holding different icon-buttons.
- * @returns {ReactElement} The BannerLinks component.
- */
-const BannerLinks = (): ReactElement => {
- return (
-
-
-
- );
-};
-
-export default BannerLinks;
diff --git a/frontend/src/components/BannerLinks/index.tsx b/frontend/src/components/BannerLinks/index.tsx
deleted file mode 100644
index 6adbefe2d..000000000
--- a/frontend/src/components/BannerLinks/index.tsx
+++ /dev/null
@@ -1,3 +0,0 @@
-import BannerLinks from './BannerLinks';
-
-export default BannerLinks;
diff --git a/frontend/src/components/BannerLogo/BannerLogo.spec.tsx b/frontend/src/components/BannerLogo/BannerLogo.spec.tsx
index 0197f52ce..a880ae4a8 100644
--- a/frontend/src/components/BannerLogo/BannerLogo.spec.tsx
+++ b/frontend/src/components/BannerLogo/BannerLogo.spec.tsx
@@ -16,26 +16,15 @@ describe('BannerLogo', () => {
expect(vonageLogo.src).toContain('/images/vonage-logo-desktop.svg');
});
- it('renders the vonage mobile logo', () => {
+ it('renders clickable logo with cursor pointer', () => {
render(
);
- const vonageLogo = screen.getByAltText('Vonage-mobile-logo');
- expect(vonageLogo).toBeInTheDocument();
- expect(vonageLogo.src).toContain('/images/vonage-logo-mobile.svg');
- });
-
- it('wraps logos in a Link pointing to the landing page', () => {
- render(
-
-
-
- );
-
- const link = screen.getByRole('link');
- expect(link).toHaveAttribute('href', '/');
+ const logo = screen.getByTestId('banner-logo-image');
+ expect(logo).toBeInTheDocument();
+ expect(logo).toHaveStyle({ cursor: 'pointer' });
});
});
diff --git a/frontend/src/components/BannerLogo/BannerLogo.tsx b/frontend/src/components/BannerLogo/BannerLogo.tsx
index 51bc69c7f..b081a16fa 100644
--- a/frontend/src/components/BannerLogo/BannerLogo.tsx
+++ b/frontend/src/components/BannerLogo/BannerLogo.tsx
@@ -1,31 +1,52 @@
import { ReactElement } from 'react';
-import { Link } from 'react-router-dom';
+import { useNavigate } from 'react-router-dom';
+import Box from '@ui/Box';
+import useIsTabletViewport from '@hooks/useIsTabletViewport';
/**
* BannerLogo Component
*
- * This component returns the logo that redirects to the landing page when clicked.
- * @returns {ReactElement} - the banner logo component
+ * This component returns a logo for the banner that navigates to the parent route when clicked.
+ * @returns {ReactElement} - the banner logo component.
*/
-const BannerLogo = (): ReactElement => (
-
-
-
![]()
{
+ const isTablet = useIsTabletViewport();
+ const navigate = useNavigate();
+ const handleClick = () => {
+ navigate('..');
+ };
+
+ return (
+
+ {
+ if (e.key === 'Enter' || e.key === ' ') {
+ handleClick();
+ }
+ }}
+ role="button"
+ tabIndex={0}
+ sx={{
+ height: { xs: 40, md: 72 },
+ display: 'block',
+ cursor: 'pointer',
}}
- className="hidden h-[72px] pl-4 pr-8 md:flex"
- src="/images/vonage-logo-desktop.svg"
- alt="Vonage-desktop-logo"
- />
-
-
-
-);
+
+ );
+};
export default BannerLogo;
diff --git a/frontend/src/components/Footer/Footer.spec.tsx b/frontend/src/components/Footer/Footer.spec.tsx
new file mode 100644
index 000000000..f4f40a85f
--- /dev/null
+++ b/frontend/src/components/Footer/Footer.spec.tsx
@@ -0,0 +1,17 @@
+import { describe, expect, it } from 'vitest';
+import { render, screen } from '@testing-library/react';
+import { MemoryRouter } from 'react-router-dom';
+import Footer from './Footer';
+
+describe('Footer', () => {
+ it('renders the footer content', () => {
+ render(
+
+
+
+ );
+
+ const footerLinks = screen.getByTestId('footer-content');
+ expect(footerLinks).toBeInTheDocument();
+ });
+});
diff --git a/frontend/src/components/Footer/Footer.tsx b/frontend/src/components/Footer/Footer.tsx
new file mode 100644
index 000000000..29f8da641
--- /dev/null
+++ b/frontend/src/components/Footer/Footer.tsx
@@ -0,0 +1,48 @@
+import { ReactElement } from 'react';
+import Box from '@ui/Box';
+import FooterLinks from '@components/FooterLinks';
+import Stack from '@ui/Stack';
+import useCustomTheme from '@Context/Theme';
+
+/**
+ * Footer Component
+ *
+ * This component returns a footer that includes a logo, current date/time, language selector, and some links.
+ * @returns {ReactElement} - the footer component.
+ */
+const Footer = (): ReactElement => {
+ const theme = useCustomTheme();
+
+ return (
+
+
+
+
+
+
+
+ );
+};
+
+export default Footer;
diff --git a/frontend/src/components/Footer/index.tsx b/frontend/src/components/Footer/index.tsx
new file mode 100644
index 000000000..ced11e525
--- /dev/null
+++ b/frontend/src/components/Footer/index.tsx
@@ -0,0 +1,3 @@
+import Footer from './Footer';
+
+export default Footer;
diff --git a/frontend/src/components/FooterLinks/FooterLinks.spec.tsx b/frontend/src/components/FooterLinks/FooterLinks.spec.tsx
new file mode 100644
index 000000000..8470780bf
--- /dev/null
+++ b/frontend/src/components/FooterLinks/FooterLinks.spec.tsx
@@ -0,0 +1,17 @@
+import { describe, expect, it } from 'vitest';
+import { render, screen } from '@testing-library/react';
+import { MemoryRouter } from 'react-router-dom';
+import FooterLinks from './FooterLinks';
+
+describe('FooterLinks', () => {
+ it('renders the footer links component', () => {
+ render(
+
+
+
+ );
+
+ const footerLinks = screen.getByTestId('footer-links');
+ expect(footerLinks).toBeInTheDocument();
+ });
+});
diff --git a/frontend/src/components/FooterLinks/FooterLinks.tsx b/frontend/src/components/FooterLinks/FooterLinks.tsx
new file mode 100644
index 000000000..d7538d949
--- /dev/null
+++ b/frontend/src/components/FooterLinks/FooterLinks.tsx
@@ -0,0 +1,35 @@
+import { ReactElement } from 'react';
+import Stack from '@ui/Stack';
+import Typography from '@ui/Typography';
+import { useTranslation } from 'react-i18next';
+import useCustomTheme from '@Context/Theme';
+import GHRepoButton from '../GHRepoButton';
+
+/**
+ * FooterLinks Component
+ *
+ * Component holding different icon-buttons.
+ * @returns {ReactElement} The FooterLinks component.
+ */
+const FooterLinks = (): ReactElement => {
+ const { t } = useTranslation();
+ const theme = useCustomTheme();
+
+ return (
+
+
+
+ {t('footer.github.title')}
+
+
+ );
+};
+
+export default FooterLinks;
diff --git a/frontend/src/components/FooterLinks/index.tsx b/frontend/src/components/FooterLinks/index.tsx
new file mode 100644
index 000000000..0566b0875
--- /dev/null
+++ b/frontend/src/components/FooterLinks/index.tsx
@@ -0,0 +1,3 @@
+import FooterLinks from './FooterLinks';
+
+export default FooterLinks;
diff --git a/frontend/src/components/GHRepoButton/GHRepoButton.spec.tsx b/frontend/src/components/GHRepoButton/GHRepoButton.spec.tsx
index cbb6d43a9..ec85b8592 100644
--- a/frontend/src/components/GHRepoButton/GHRepoButton.spec.tsx
+++ b/frontend/src/components/GHRepoButton/GHRepoButton.spec.tsx
@@ -26,7 +26,7 @@ describe('GHRepoButton', () => {
it('renders the GitHub icon', () => {
render();
- const icon = screen.getByTestId('GitHubIcon');
+ const icon = screen.getByTestId('vivid-icon-github-mono');
expect(icon).toBeInTheDocument();
});
});
diff --git a/frontend/src/components/GHRepoButton/GHRepoButton.tsx b/frontend/src/components/GHRepoButton/GHRepoButton.tsx
index 08d39d220..d06551fb2 100644
--- a/frontend/src/components/GHRepoButton/GHRepoButton.tsx
+++ b/frontend/src/components/GHRepoButton/GHRepoButton.tsx
@@ -1,7 +1,9 @@
-import { IconButton, Link, Tooltip } from '@mui/material';
-import { GitHub as GitHubIcon } from '@mui/icons-material';
import { ReactElement } from 'react';
import { useTranslation } from 'react-i18next';
+import Tooltip from '@ui/Tooltip';
+import Link from '@ui/Link';
+import IconButton from '@ui/IconButton';
+import VividIcon from '@components/VividIcon';
/**
* GHRepoButton Component
@@ -15,8 +17,8 @@ const GHRepoButton = (): ReactElement => {
return (
-
-
+
+
diff --git a/frontend/src/components/LandingPageWelcome/LandingPageWelcome.tsx b/frontend/src/components/LandingPageWelcome/LandingPageWelcome.tsx
index fa737636e..c2f01d076 100644
--- a/frontend/src/components/LandingPageWelcome/LandingPageWelcome.tsx
+++ b/frontend/src/components/LandingPageWelcome/LandingPageWelcome.tsx
@@ -37,7 +37,7 @@ const LandingPageWelcome = (): ReactElement => {
sx={{
maxWidth: '48rem',
pl: { xs: 0, lg: 4 },
- mb: { xs: 0, md: 6 },
+ mb: { xs: 0, md: 16 },
ml: { xs: 0, md: 2 },
textAlign: 'left',
}}
diff --git a/frontend/src/components/LanguageSelector/LanguageSelector.spec.tsx b/frontend/src/components/LanguageSelector/LanguageSelector.spec.tsx
index 7edcc9fe9..275baecef 100644
--- a/frontend/src/components/LanguageSelector/LanguageSelector.spec.tsx
+++ b/frontend/src/components/LanguageSelector/LanguageSelector.spec.tsx
@@ -82,22 +82,13 @@ describe('LanguageSelector', () => {
expect(screen.queryByTestId('vivid-icon-flag-united-kingdom')).not.toBeInTheDocument();
});
- it('applies custom className', () => {
- env.setSupportedLanguages('en');
-
- render();
-
- const formControl = screen.getByTestId('language-selector').closest('.MuiFormControl-root');
- expect(formControl).toHaveClass('bg-red-500');
- });
-
it('renders VividIcon with correct size in main display', () => {
env.setSupportedLanguages('en');
render();
const icon = screen.getByTestId('vivid-icon-flag-united-kingdom');
- expect(icon).toHaveAttribute('data-size', '-3');
+ expect(icon).toHaveAttribute('data-size', '-2');
});
});
@@ -319,13 +310,12 @@ describe('LanguageSelector', () => {
const spanishOption = screen.getByTestId('language-option-es');
expect(spanishOption).toHaveTextContent('Español');
- // Check for flag icons in dropdown (they should have size -5)
const englishIcon = screen
.getAllByTestId('vivid-icon-flag-united-kingdom')
- .find((icon) => icon.getAttribute('data-size') === '-5');
+ .find((icon) => icon.getAttribute('data-size') === '-2');
const spanishIcon = screen
.getAllByTestId('vivid-icon-flag-spain')
- .find((icon) => icon.getAttribute('data-size') === '-5');
+ .find((icon) => icon.getAttribute('data-size') === '-2');
expect(englishIcon).toBeInTheDocument();
expect(spanishIcon).toBeInTheDocument();
@@ -357,10 +347,9 @@ describe('LanguageSelector', () => {
fireEvent.mouseDown(selectButton);
await waitFor(() => {
- // Check that dropdown icons have size -5
const dropdownIcon = screen
.getAllByTestId('vivid-icon-flag-united-kingdom')
- .find((icon) => icon.getAttribute('data-size') === '-5');
+ .find((icon) => icon.getAttribute('data-size') === '-2');
expect(dropdownIcon).toBeInTheDocument();
});
});
@@ -372,17 +361,15 @@ describe('LanguageSelector', () => {
render();
- // Main display should have size -3
const displayIcon = screen.getByTestId('vivid-icon-flag-united-kingdom');
- expect(displayIcon).toHaveAttribute('data-size', '-3');
+ expect(displayIcon).toHaveAttribute('data-size', '-2');
const selectButton = screen.getByRole('combobox');
fireEvent.mouseDown(selectButton);
await waitFor(() => {
- // Dropdown should have size -5 (smaller)
const dropdownIcons = screen.getAllByTestId('vivid-icon-flag-united-kingdom');
- const dropdownIcon = dropdownIcons.find((icon) => icon.getAttribute('data-size') === '-5');
+ const dropdownIcon = dropdownIcons.find((icon) => icon.getAttribute('data-size') === '-2');
expect(dropdownIcon).toBeInTheDocument();
});
});
diff --git a/frontend/src/components/LanguageSelector/LanguageSelector.tsx b/frontend/src/components/LanguageSelector/LanguageSelector.tsx
index 6a18ca829..201860923 100644
--- a/frontend/src/components/LanguageSelector/LanguageSelector.tsx
+++ b/frontend/src/components/LanguageSelector/LanguageSelector.tsx
@@ -1,21 +1,15 @@
import { ReactElement } from 'react';
-import { Select, MenuItem, FormControl, SelectChangeEvent, Box } from '@mui/material';
import { useTranslation } from 'react-i18next';
+import Box from '@ui/Box';
+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 { LanguageOption, LanguageSelectorProps } from './LanguageSelector.types';
import useIsSmallViewport from '../../hooks/useIsSmallViewport';
import VividIcon from '../VividIcon/VividIcon';
-import env, { type Lang } from '../../env';
-
-type LanguageOption = {
- code: Lang;
- name: string;
- flag: string;
-};
-
-type LanguageSelectorProps = {
- showFlag?: boolean;
- className?: string;
-};
+import env from '../../env';
const languageOptions: LanguageOption[] = [
{ code: 'en', name: 'English', flag: 'flag-united-kingdom' },
@@ -31,10 +25,9 @@ const languageOptions: LanguageOption[] = [
* The available languages are determined by the VITE_I18N_SUPPORTED_LANGUAGES environment variable.
* @param {LanguageSelectorProps} props - The props for the component.
* @property {boolean} showFlag - Whether to display the country flag alongside the language name.
- * @property {string} className - Additional CSS classes to apply to the component.
* @returns {ReactElement} The rendered LanguageSelector component.
*/
-const LanguageSelector = ({ showFlag = true, className }: LanguageSelectorProps): ReactElement => {
+const LanguageSelector = ({ showFlag = true }: LanguageSelectorProps): ReactElement => {
const { i18n } = useTranslation();
const theme = useCustomTheme();
const isSmallViewport = useIsSmallViewport();
@@ -51,7 +44,7 @@ const LanguageSelector = ({ showFlag = true, className }: LanguageSelectorProps)
const currentLanguage = i18n.language || 'en';
return (
-
+