From 4dfdec81031a0814b5131e19980526707280b5e4 Mon Sep 17 00:00:00 2001 From: Iryna Lypnyk Date: Sat, 2 May 2026 20:17:14 +0100 Subject: [PATCH 1/2] fix: standardise breadcrumbs across mentorship pages (#201) - unify usage of BreadCrumbsDynamic - remove inconsistent isMobile conditions - replace custom breadcrumbs with shared component - add consistent spacing to breadcrumbs - update tests to mock router pathname --- .../pages/MenteeRegistrationPage.test.tsx | 6 +- src/components/BreadCrumbsDynamic.tsx | 1 + src/pages/mentorship/ad-hoc-timeline.tsx | 2 +- src/pages/mentorship/code-of-conduct.tsx | 4 +- src/pages/mentorship/faqs.tsx | 4 +- src/pages/mentorship/index.tsx | 4 +- src/pages/mentorship/long-term-timeline.tsx | 2 +- src/pages/mentorship/mentee-registration.tsx | 22 +- src/pages/mentorship/mentor-registration.tsx | 421 +++++++++--------- src/pages/mentorship/mentors.tsx | 2 +- src/pages/mentorship/resources.tsx | 4 +- src/pages/mentorship/study-groups.tsx | 4 +- 12 files changed, 232 insertions(+), 244 deletions(-) diff --git a/src/__tests__/pages/MenteeRegistrationPage.test.tsx b/src/__tests__/pages/MenteeRegistrationPage.test.tsx index c46e2af6..afc33e9b 100644 --- a/src/__tests__/pages/MenteeRegistrationPage.test.tsx +++ b/src/__tests__/pages/MenteeRegistrationPage.test.tsx @@ -19,7 +19,11 @@ jest.mock('next/link', () => { }); jest.mock('next/router', () => ({ - useRouter: () => ({ push: jest.fn(), pathname: '/' }), + useRouter: () => ({ + push: jest.fn(), + pathname: '/mentorship/mentee-registration', + query: {}, + }), })); // Mutable flag so individual tests can override the registration state diff --git a/src/components/BreadCrumbsDynamic.tsx b/src/components/BreadCrumbsDynamic.tsx index b79ad498..c0ca4cb2 100644 --- a/src/components/BreadCrumbsDynamic.tsx +++ b/src/components/BreadCrumbsDynamic.tsx @@ -18,6 +18,7 @@ export const BreadCrumbsDynamic = () => { margin: 0, width: '100%', mx: 'auto', + px: 2, boxSizing: 'border-box', maxWidth: '1128px', }} diff --git a/src/pages/mentorship/ad-hoc-timeline.tsx b/src/pages/mentorship/ad-hoc-timeline.tsx index 77bedc33..5cc09a60 100644 --- a/src/pages/mentorship/ad-hoc-timeline.tsx +++ b/src/pages/mentorship/ad-hoc-timeline.tsx @@ -26,7 +26,7 @@ const MentorshipAdHocTimelinePage = ({ data, footer }: CombinedResponse) => { return ( <> - {isMobile ? null : } + <Box sx={theme.custom.containerBox}> <Timeline position="right"> diff --git a/src/pages/mentorship/code-of-conduct.tsx b/src/pages/mentorship/code-of-conduct.tsx index 069b4f65..be5face6 100644 --- a/src/pages/mentorship/code-of-conduct.tsx +++ b/src/pages/mentorship/code-of-conduct.tsx @@ -4,7 +4,6 @@ import { Box, Typography } from '@mui/material'; import { GetServerSideProps } from 'next'; import { BreadCrumbsDynamic, CodeOfConductSection } from '@components'; -import { useIsMobile } from '@utils/theme-utils'; import { MentorshipCodeOfConductData } from '@utils/types'; import { fetchData } from 'lib/api'; @@ -15,10 +14,9 @@ interface AboutUsCodeOfConductPageProps { const MentorshipCodeOfConductPage = ({ mentorshipCodeOfConduct, }: AboutUsCodeOfConductPageProps) => { - const isMobile = useIsMobile(); return ( <> - {isMobile ? null : <BreadCrumbsDynamic />} + <BreadCrumbsDynamic /> <Box sx={{ display: 'flex', diff --git a/src/pages/mentorship/faqs.tsx b/src/pages/mentorship/faqs.tsx index 24303e7a..01165b70 100644 --- a/src/pages/mentorship/faqs.tsx +++ b/src/pages/mentorship/faqs.tsx @@ -3,7 +3,6 @@ import { GetServerSideProps } from 'next'; import React from 'react'; import { BreadCrumbsDynamic, FaqSection } from '@components'; -import { useIsMobile } from '@utils/theme-utils'; import { fetchData } from 'lib/api'; import { MentorshipPageData } from '../../utils/types'; @@ -21,11 +20,10 @@ const MentorshipFaqsPage = ({ data }: FaqsPageProps) => { } = data; const faqSections = [commonFaqSection, mentorsFaqSection, menteesFaqSection]; - const isMobile = useIsMobile(); return ( <> - {isMobile ? <BreadCrumbsDynamic /> : null} + <BreadCrumbsDynamic /> <Box sx={{ backgroundImage: 'linear-gradient(to right, #9FCEEC, #C7E7FF)', diff --git a/src/pages/mentorship/index.tsx b/src/pages/mentorship/index.tsx index 7a9f2116..640dc228 100644 --- a/src/pages/mentorship/index.tsx +++ b/src/pages/mentorship/index.tsx @@ -10,7 +10,6 @@ import { MentorBecomeCard, Title, } from '@components'; -import { useIsMobile } from '@utils/theme-utils'; import { FooterResponse, MentorshipProgrammeData, @@ -30,10 +29,9 @@ interface FeedbackSectionProps { feedbacks: FeedbackItem[]; } const MentorshipPage = ({ mentorship, footer }: MentorshipPageProps) => { - const isMobile = useIsMobile(); return ( <> - {isMobile ? null : <BreadCrumbsDynamic />} + <BreadCrumbsDynamic /> <Title title={mentorship.heroSection.title} /> <Box diff --git a/src/pages/mentorship/long-term-timeline.tsx b/src/pages/mentorship/long-term-timeline.tsx index b86d70fe..b93733ae 100644 --- a/src/pages/mentorship/long-term-timeline.tsx +++ b/src/pages/mentorship/long-term-timeline.tsx @@ -24,7 +24,7 @@ const MentorshipLongTermTimelinePage = ({ data, footer }: CombinedResponse) => { const isMobile = useIsMobile(); return ( <> - {isMobile ? null : <BreadCrumbsDynamic />} + <BreadCrumbsDynamic /> <Title title={'Long-Term Mentorship Timeline'} /> <Box sx={theme.custom.containerBox}> <Timeline position="right"> diff --git a/src/pages/mentorship/mentee-registration.tsx b/src/pages/mentorship/mentee-registration.tsx index ac85c4bf..60bc6300 100644 --- a/src/pages/mentorship/mentee-registration.tsx +++ b/src/pages/mentorship/mentee-registration.tsx @@ -2,10 +2,8 @@ import { zodResolver } from '@hookform/resolvers/zod'; import { Alert, Box, - Breadcrumbs, Button, Container, - Link, Paper, Stack, Typography, @@ -16,6 +14,7 @@ import NextLink from 'next/link'; import React, { useEffect, useState } from 'react'; import { FormProvider, UseFormReturn, useForm } from 'react-hook-form'; +import { BreadCrumbsDynamic } from '@components'; import { menteeFormDefaultValues, menteeFormSchema, @@ -175,24 +174,7 @@ const MenteeRegistrationPage = () => { return ( <> - <Box - sx={{ - backgroundColor: 'white', - py: 2, - px: { xs: 2, sm: 3, md: '157px' }, - }} - > - <Breadcrumbs> - <Link href="/" color="primary" underline="always"> - Home - </Link> - <Link href="/mentorship" color="primary" underline="always"> - Mentorship - </Link> - <Typography color="text.primary">Mentee Registration</Typography> - </Breadcrumbs> - </Box> - + <BreadCrumbsDynamic /> <FormProvider {...formMethods}> <Box sx={{ diff --git a/src/pages/mentorship/mentor-registration.tsx b/src/pages/mentorship/mentor-registration.tsx index 6b758ee6..55bf9849 100644 --- a/src/pages/mentorship/mentor-registration.tsx +++ b/src/pages/mentorship/mentor-registration.tsx @@ -15,6 +15,7 @@ import Link from 'next/link'; import React, { useState } from 'react'; import { useForm, FormProvider, type Resolver } from 'react-hook-form'; +import { BreadCrumbsDynamic } from '@components'; import Step1BasicInfo from 'components/mentorship/Step1BasicInfo'; import Step2Skills from 'components/mentorship/Step2Skills'; import Step3DomainSkills from 'components/mentorship/Step3DomainSkills'; @@ -195,248 +196,258 @@ const MentorRegistrationPage = () => { if (submissionStatus === 'success') { return ( - <Box - sx={{ - minHeight: '100vh', - bgcolor: 'custom.lightBlue', - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - px: 2, - }} - > - <Paper - elevation={3} + <> + <BreadCrumbsDynamic /> + <Box sx={{ - p: { xs: 4, sm: 6 }, - borderRadius: 2, - maxWidth: '540px', - textAlign: 'center', + minHeight: '100vh', + bgcolor: 'custom.lightBlue', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + px: 2, }} > - <Typography variant="h4" gutterBottom color="success.main"> - Application Submitted! - </Typography> - <Typography variant="body1" sx={{ mb: 4 }}> - Thank you for applying to be a mentor. Your application has been - received and is now being reviewed. We will get back to you soon. - </Typography> - <Link href="/mentorship" passHref> - <Button variant="contained" color="primary"> - Go to Mentorship Page - </Button> - </Link> - </Paper> - </Box> + <Paper + elevation={3} + sx={{ + p: { xs: 4, sm: 6 }, + borderRadius: 2, + maxWidth: '540px', + textAlign: 'center', + }} + > + <Typography variant="h4" gutterBottom color="success.main"> + Application Submitted! + </Typography> + <Typography variant="body1" sx={{ mb: 4 }}> + Thank you for applying to be a mentor. Your application has been + received and is now being reviewed. We will get back to you soon. + </Typography> + <Link href="/mentorship" passHref> + <Button variant="contained" color="primary"> + Go to Mentorship Page + </Button> + </Link> + </Paper> + </Box> + </> ); } return ( - <FormProvider {...formMethods}> - <Box - sx={{ - minHeight: '100vh', - bgcolor: 'custom.lightBlue', - position: 'relative', - overflow: 'hidden', - pb: 8, - }} - > - <Container - maxWidth={false} + <> + <BreadCrumbsDynamic /> + <FormProvider {...formMethods}> + <Box sx={{ + minHeight: '100vh', + bgcolor: 'custom.lightBlue', position: 'relative', - zIndex: 1, - pt: { xs: 4, sm: 10, md: '18.75rem' }, - px: { xs: 2, sm: 3 }, - maxWidth: isMobile ? '100%' : theme.custom.innerBox.maxWidth, - margin: '0 auto', + overflow: 'hidden', + pb: 8, }} > - <Box - component="img" - src="/mentor-hero-bg.png" - alt="Mentor background" - sx={{ - position: 'absolute', - top: '-6.25rem', - right: 0, - height: { xs: '220px', sm: '280px', md: '360px', lg: '420px' }, - width: 'auto', - zIndex: -1, - opacity: 0.9, - pointerEvents: 'none', - }} - /> - <Paper - elevation={3} + <Container + maxWidth={false} sx={{ - p: { xs: 3, sm: 4, md: 5 }, - borderRadius: 2, - width: '100%', - maxWidth: { xs: '100%', sm: '540px', md: '640px' }, - mx: 'auto', - bgcolor: 'white', + position: 'relative', + zIndex: 1, + pt: { xs: 4, sm: 10, md: '18.75rem' }, + px: { xs: 2, sm: 3 }, + maxWidth: isMobile ? '100%' : theme.custom.innerBox.maxWidth, + margin: '0 auto', }} > - <Typography - variant="body2" - align="center" + <Box + component="img" + src="/mentor-hero-bg.png" + alt="Mentor background" sx={{ - mb: 3, - color: 'text.secondary', + position: 'absolute', + top: '-6.25rem', + right: 0, + height: { xs: '220px', sm: '280px', md: '360px', lg: '420px' }, + width: 'auto', + zIndex: -1, + opacity: 0.9, + pointerEvents: 'none', }} - > - Step {activeStep} of {totalSteps} - </Typography> - - {submissionStatus === 'error' && ( - <Alert severity="error" sx={{ mb: 3 }}> - {errorMessage} - </Alert> - )} - - <Box + /> + <Paper + elevation={3} sx={{ + p: { xs: 3, sm: 4, md: 5 }, + borderRadius: 2, width: '100%', - height: 6, - bgcolor: '#E5E5E5', - borderRadius: 3, - mb: 5, - overflow: 'hidden', + maxWidth: { xs: '100%', sm: '540px', md: '640px' }, + mx: 'auto', + bgcolor: 'white', }} > - <Box + <Typography + variant="body2" + align="center" sx={{ - width: `${(activeStep / totalSteps) * 100}%`, - height: '100%', - bgcolor: 'primary.main', - borderRadius: 3, - transition: 'width 0.3s ease', + mb: 3, + color: 'text.secondary', }} - /> - </Box> + > + Step {activeStep} of {totalSteps} + </Typography> - <Box> - {activeStep === 1 && <Step1BasicInfo />} - {activeStep === 2 && <Step2Skills />} - {activeStep === 3 && <Step3DomainSkills />} - {activeStep === 4 && <Step4ProgrammingSkills />} - {activeStep === 5 && <Step5Review />} - </Box> + {submissionStatus === 'error' && ( + <Alert severity="error" sx={{ mb: 3 }}> + {errorMessage} + </Alert> + )} - <Stack - direction="row" - justifyContent="space-between" - alignItems="center" - mt={5} - spacing={2} - > - <Button - variant="outlined" - disabled={activeStep === 1 || submissionStatus === 'loading'} - onClick={handleBack} + <Box sx={{ - px: { xs: 2.5, md: 3.5 }, - py: 1, + width: '100%', + height: 6, + bgcolor: '#E5E5E5', + borderRadius: 3, + mb: 5, + overflow: 'hidden', }} > - Back - </Button> + <Box + sx={{ + width: `${(activeStep / totalSteps) * 100}%`, + height: '100%', + bgcolor: 'primary.main', + borderRadius: 3, + transition: 'width 0.3s ease', + }} + /> + </Box> - <Box sx={{ flexGrow: 1, textAlign: 'center' }}> - {Object.keys(formMethods.formState.errors).length > 0 && ( - <Box sx={{ mb: 2 }}> - <Typography color="error" variant="subtitle2" gutterBottom> - Please fix the following validation errors: - </Typography> - <ul - style={{ - textAlign: 'left', - color: theme.palette.error.main, - margin: '0 auto', - display: 'inline-block', - }} - > - {Object.entries(formMethods.formState.errors).map( - ([key, error]: [string, any]) => { - const label = - { - fullName: 'Full Name', - email: 'Email', - slackDisplayName: 'Slack Name', - country: 'Country', - city: 'City', - position: 'Position', - companyName: 'Company Name', - calendlyLink: 'Calendly Link', - menteeExpectations: 'Mentee Expectations', - openToNonWomen: 'Open to non-women', - isLongTermMentor: 'Mentorship Format', - maxMentees: 'Max Mentees', - adHocAvailability: 'Ad-hoc Availability', - languages: 'Languages', - yearsExperience: 'Years of Experience', - bio: 'Bio', - technicalAreas: 'Technical Areas', - codeLanguages: 'Programming Languages', - mentorshipFocusAreas: 'Mentorship Focus Areas', - linkedin: 'LinkedIn', - identity: 'Identity', - pronouns: 'Pronouns', - socialHighlight: 'Social Highlight', - termsAgreed: 'Terms Agreement', - }[key] || key; - return ( - <li key={key}> - <Typography variant="caption"> - <strong>{label}:</strong>{' '} - {error?.message || 'Invalid value'} - </Typography> - </li> - ); - }, - )} - </ul> - </Box> - )} + <Box> + {activeStep === 1 && <Step1BasicInfo />} + {activeStep === 2 && <Step2Skills />} + {activeStep === 3 && <Step3DomainSkills />} + {activeStep === 4 && <Step4ProgrammingSkills />} + {activeStep === 5 && <Step5Review />} </Box> - {activeStep === totalSteps ? ( - <Button - variant="contained" - color="success" - disabled={submissionStatus === 'loading'} - onClick={formMethods.handleSubmit(onSubmit, onInvalid)} - sx={{ - px: { xs: 2.5, md: 3.5 }, - py: 1, - minWidth: '120px', - }} - > - {submissionStatus === 'loading' ? ( - <CircularProgress size={24} color="inherit" /> - ) : ( - 'Submit Application' - )} - </Button> - ) : ( + <Stack + direction="row" + justifyContent="space-between" + alignItems="center" + mt={5} + spacing={2} + > <Button - variant="contained" - onClick={handleNext} + variant="outlined" + disabled={activeStep === 1 || submissionStatus === 'loading'} + onClick={handleBack} sx={{ px: { xs: 2.5, md: 3.5 }, py: 1, }} > - Next + Back </Button> - )} - </Stack> - </Paper> - </Container> - </Box> - </FormProvider> + + <Box sx={{ flexGrow: 1, textAlign: 'center' }}> + {Object.keys(formMethods.formState.errors).length > 0 && ( + <Box sx={{ mb: 2 }}> + <Typography + color="error" + variant="subtitle2" + gutterBottom + > + Please fix the following validation errors: + </Typography> + <ul + style={{ + textAlign: 'left', + color: theme.palette.error.main, + margin: '0 auto', + display: 'inline-block', + }} + > + {Object.entries(formMethods.formState.errors).map( + ([key, error]: [string, any]) => { + const label = + { + fullName: 'Full Name', + email: 'Email', + slackDisplayName: 'Slack Name', + country: 'Country', + city: 'City', + position: 'Position', + companyName: 'Company Name', + calendlyLink: 'Calendly Link', + menteeExpectations: 'Mentee Expectations', + openToNonWomen: 'Open to non-women', + isLongTermMentor: 'Mentorship Format', + maxMentees: 'Max Mentees', + adHocAvailability: 'Ad-hoc Availability', + languages: 'Languages', + yearsExperience: 'Years of Experience', + bio: 'Bio', + technicalAreas: 'Technical Areas', + codeLanguages: 'Programming Languages', + mentorshipFocusAreas: 'Mentorship Focus Areas', + linkedin: 'LinkedIn', + identity: 'Identity', + pronouns: 'Pronouns', + socialHighlight: 'Social Highlight', + termsAgreed: 'Terms Agreement', + }[key] || key; + return ( + <li key={key}> + <Typography variant="caption"> + <strong>{label}:</strong>{' '} + {error?.message || 'Invalid value'} + </Typography> + </li> + ); + }, + )} + </ul> + </Box> + )} + </Box> + + {activeStep === totalSteps ? ( + <Button + variant="contained" + color="success" + disabled={submissionStatus === 'loading'} + onClick={formMethods.handleSubmit(onSubmit, onInvalid)} + sx={{ + px: { xs: 2.5, md: 3.5 }, + py: 1, + minWidth: '120px', + }} + > + {submissionStatus === 'loading' ? ( + <CircularProgress size={24} color="inherit" /> + ) : ( + 'Submit Application' + )} + </Button> + ) : ( + <Button + variant="contained" + onClick={handleNext} + sx={{ + px: { xs: 2.5, md: 3.5 }, + py: 1, + }} + > + Next + </Button> + )} + </Stack> + </Paper> + </Container> + </Box> + </FormProvider> + </> ); }; diff --git a/src/pages/mentorship/mentors.tsx b/src/pages/mentorship/mentors.tsx index 9b4a16c0..be64f478 100644 --- a/src/pages/mentorship/mentors.tsx +++ b/src/pages/mentorship/mentors.tsx @@ -161,7 +161,7 @@ const MentorsPage = () => { return ( <> - {isMobile ? null : <BreadCrumbsDynamic />} + <BreadCrumbsDynamic /> <Title title="Meet Our Mentors" /> <Box sx={theme.custom.containerBox}> {/* Filter / Search bar */} diff --git a/src/pages/mentorship/resources.tsx b/src/pages/mentorship/resources.tsx index 08babfb7..53809dcc 100644 --- a/src/pages/mentorship/resources.tsx +++ b/src/pages/mentorship/resources.tsx @@ -4,7 +4,6 @@ import { GetServerSideProps } from 'next'; import React from 'react'; import { Title, ResourcesCard, Footer, BreadCrumbsDynamic } from '@components'; -import { useIsMobile } from '@utils/theme-utils'; import { FooterResponse, MentorshipResourcesResponse } from '@utils/types'; import { fetchData } from 'lib/api'; import footerData from 'lib/responses/footer.json'; @@ -16,12 +15,11 @@ type CombinedResponse = { }; const MentorshipResourcesPage: React.FC = () => { - const isMobile = useIsMobile(); const { heroTitle, heroDescription, resources } = pageData; return ( <> - {isMobile ? null : <BreadCrumbsDynamic />} + <BreadCrumbsDynamic /> <Box sx={{ display: 'flex', flexDirection: 'column', minHeight: '100vh' }} diff --git a/src/pages/mentorship/study-groups.tsx b/src/pages/mentorship/study-groups.tsx index 200dfff4..f3f7418b 100644 --- a/src/pages/mentorship/study-groups.tsx +++ b/src/pages/mentorship/study-groups.tsx @@ -9,7 +9,6 @@ import { Footer, BreadCrumbsDynamic, } from '@components'; -import { useIsMobile } from '@utils/theme-utils'; import { FooterResponse, StudyGroupsPageData } from '@utils/types'; import { fetchData } from 'lib/api'; @@ -28,11 +27,10 @@ const MentorShipStudyGroupsPage = ({ data, footer }: StudyGroupsPageProps) => { const muiTheme = useTheme(); const cardColors = muiTheme.palette.custom.studyGroupCardColors; - const isMobile = useIsMobile(); return ( <Box> - {isMobile ? null : <BreadCrumbsDynamic />} + <BreadCrumbsDynamic /> <HeroWithImage title={data.heroSection.title} imageSrc={'/hero-img.jpg'} // @TODO replace with actual path? From 4b38833140cd5dc7bbe8af247d25ccb33ffb4ab9 Mon Sep 17 00:00:00 2001 From: Iryna Lypnyk <iryna.lypnyk@gmail.com> Date: Sun, 3 May 2026 21:00:38 +0100 Subject: [PATCH 2/2] test: fix outdated mentor registration page text assertion (#201) --- playwright-tests/tests/home.page.spec.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/playwright-tests/tests/home.page.spec.ts b/playwright-tests/tests/home.page.spec.ts index a624bc1a..a13bcee3 100644 --- a/playwright-tests/tests/home.page.spec.ts +++ b/playwright-tests/tests/home.page.spec.ts @@ -89,9 +89,7 @@ test.describe('Validate Home Page', () => { await basePage.clickElement(homePage.joinAsMentorBtn); await basePage.verifyURL('/mentorship/mentor-registration'); - await basePage.verifyPageContainsText( - 'Welcome to the MentorRegistrationPage', - ); + await basePage.verifyPageContainsText('WCC: Registration Form for Mentors'); }); test('HP-004: Volunteer section', async ({ homePage, basePage }) => {