-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(services-jeunes): crée la page simple (#3307)
* feat(services-jeunes): crée la page simple * fix(a11y): corrige l'inclusion de l'intitulé du bouton dans le nom accessible
- Loading branch information
Showing
6 changed files
with
317 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import { PageTags } from '~/client/services/analytics/analytics'; | ||
|
||
const analyticsPageConfig: PageTags = { | ||
page_template: 'contenu_liste_niv_1', | ||
pagegroup: 'service_jeune_liste', | ||
pagelabel: 'contenu_liste_niv_1', | ||
'segment-site': 'contenu_liste', | ||
}; | ||
|
||
export default analyticsPageConfig; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
@use "@styles/utilities-deprecated"; | ||
|
||
.section { | ||
padding-inline: 1rem; | ||
margin-bottom: 3rem; | ||
} | ||
|
||
.cartesActualitesList { | ||
display: flex; | ||
gap: 2rem; | ||
justify-content: center; | ||
flex-wrap: wrap; | ||
margin-bottom: 2rem; | ||
flex-direction: column; | ||
} | ||
|
||
.carteActualite { | ||
@include utilities-deprecated.media(medium) { | ||
width: 22.5rem; | ||
} | ||
} | ||
|
||
.carteActualiteDescription { | ||
@include utilities-deprecated.line-clamp(3); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,214 @@ | ||
/** | ||
* @jest-environment jsdom | ||
*/ | ||
import '~/test-utils'; | ||
|
||
import { render, screen, within } from '@testing-library/react'; | ||
import { userEvent } from '@testing-library/user-event'; | ||
|
||
import { mockUseRouter } from '~/client/components/useRouter.mock'; | ||
import { mockScrollIntoView, mockSmallScreen } from '~/client/components/window.mock'; | ||
import { DependenciesProvider } from '~/client/context/dependenciesContainer.context'; | ||
import { aManualAnalyticsService } from '~/client/services/analytics/analytics.service.fixture'; | ||
import ServicesJeunePage, { getStaticProps } from '~/pages/services-jeunes/index.page'; | ||
import { createFailure, createSuccess } from '~/server/errors/either'; | ||
import { ErreurMetier } from '~/server/errors/erreurMetier.types'; | ||
import { aServiceJeune, aServiceJeuneList } from '~/server/services-jeunes/domain/servicesJeunes.fixture'; | ||
import { dependencies } from '~/server/start'; | ||
|
||
|
||
jest.mock('~/server/start', () => ({ | ||
dependencies: { | ||
cmsDependencies: { | ||
duréeDeValiditéEnSecondes: jest.fn(), | ||
}, | ||
servicesJeunesDependencies: { | ||
consulterLesServicesJeunesUseCase: { | ||
handle: jest.fn(), | ||
}, | ||
}, | ||
}, | ||
})); | ||
|
||
describe('Page Services Jeunes', () => { | ||
beforeEach(() => { | ||
mockSmallScreen(); | ||
mockUseRouter({}); | ||
mockScrollIntoView(); | ||
}); | ||
afterEach(() => { | ||
jest.clearAllMocks(); | ||
}); | ||
|
||
it('lorsque le feature flip old espace jeune est actif, redirige vers la page 404', async () => { | ||
// Given | ||
process.env.NEXT_PUBLIC_OLD_ESPACE_JEUNE_FEATURE = '1'; | ||
|
||
// When | ||
const result = await getStaticProps(); | ||
|
||
// Then | ||
expect(result).toEqual({ notFound: true }); | ||
}); | ||
describe('lorsque le feature flip old espace jeune est désactivé', () => { | ||
beforeEach(() => { | ||
process.env.NEXT_PUBLIC_OLD_ESPACE_JEUNE_FEATURE = '0'; | ||
}); | ||
|
||
it('doit rendre du HTML respectant la specification', () => { | ||
// Given | ||
const serviceJeuneList = aServiceJeuneList(); | ||
mockUseRouter({}); | ||
mockSmallScreen(); | ||
|
||
// When | ||
const { container } = render(<DependenciesProvider analyticsService={aManualAnalyticsService()}> | ||
<ServicesJeunePage serviceJeuneList={serviceJeuneList} /> | ||
</DependenciesProvider>); | ||
|
||
// Then | ||
expect(container.outerHTML).toHTMLValidate(); | ||
}); | ||
it('n‘a pas de défaut d‘accessibilité', async () => { | ||
// Given | ||
const serviceJeuneList = aServiceJeuneList(); | ||
mockUseRouter({}); | ||
mockSmallScreen(); | ||
|
||
// When | ||
const { container } = render( | ||
<DependenciesProvider | ||
analyticsService={aManualAnalyticsService()}> | ||
<ServicesJeunePage | ||
serviceJeuneList={serviceJeuneList} />); | ||
</DependenciesProvider>); | ||
|
||
// Then | ||
await expect(container).toBeAccessible(); | ||
}); | ||
it('envoie les analytics de la page à son affichage', () => { | ||
// Given | ||
const serviceJeuneList = aServiceJeuneList(); | ||
const analyticsService = aManualAnalyticsService(); | ||
|
||
// When | ||
render( | ||
<DependenciesProvider | ||
analyticsService={analyticsService}> | ||
<ServicesJeunePage serviceJeuneList={serviceJeuneList} /> | ||
</DependenciesProvider>, | ||
); | ||
|
||
// Then | ||
expect(analyticsService.envoyerAnalyticsPageVue).toHaveBeenCalledWith({ | ||
page_template: 'contenu_liste_niv_1', | ||
pagegroup: 'service_jeune_liste', | ||
pagelabel: 'contenu_liste_niv_1', | ||
'segment-site': 'contenu_liste', | ||
}); | ||
}); | ||
|
||
it('appelle le serveur pour récupérer les actualités', async () => { | ||
// Given | ||
jest.spyOn(dependencies.servicesJeunesDependencies.consulterLesServicesJeunesUseCase, 'handle').mockResolvedValue(createSuccess(aServiceJeuneList())); | ||
|
||
// When | ||
await getStaticProps(); | ||
|
||
// Then | ||
expect(dependencies.servicesJeunesDependencies.consulterLesServicesJeunesUseCase.handle).toHaveBeenCalledTimes(1); | ||
}); | ||
|
||
it('quand le service est indisponible, retourne une 404', async () => { | ||
// Given | ||
jest.spyOn(dependencies.servicesJeunesDependencies.consulterLesServicesJeunesUseCase, 'handle').mockResolvedValue(createFailure(ErreurMetier.SERVICE_INDISPONIBLE)); | ||
|
||
// When | ||
const result = await getStaticProps(); | ||
|
||
// Then | ||
expect(result).toEqual({ notFound: true, revalidate: 1 }); | ||
}); | ||
|
||
describe('Si des services jeunes sont récupérés', () => { | ||
it('affiche au maximum 6 services initialement', () => { | ||
// Given | ||
const serviceJeuneList = [ | ||
aServiceJeune({ titre: 'service 1' }), | ||
aServiceJeune({ titre: 'service 2' }), | ||
aServiceJeune({ titre: 'service 3' }), | ||
aServiceJeune({ titre: 'service 4' }), | ||
aServiceJeune({ titre: 'service 5' }), | ||
aServiceJeune({ titre: 'service 6' }), | ||
aServiceJeune({ titre: 'service 7' }), | ||
]; | ||
const analyticsService = aManualAnalyticsService(); | ||
|
||
// When | ||
render( | ||
<DependenciesProvider analyticsService={analyticsService}> | ||
<ServicesJeunePage serviceJeuneList={serviceJeuneList} /> | ||
</DependenciesProvider>, | ||
); | ||
|
||
// Then | ||
const mesuresJeunesSection = screen.getByRole('region', { name: 'les services jeunes' }); | ||
const servicesJeunesList = within(mesuresJeunesSection).getAllByRole('listitem'); | ||
expect(servicesJeunesList.length).toBe(6); | ||
}); | ||
it('affiche un bouton voir plus quand il y a plus de 6 services', () => { | ||
// Given | ||
const serviceJeuneList = [ | ||
aServiceJeune({ titre: 'service 1' }), | ||
aServiceJeune({ titre: 'service 2' }), | ||
aServiceJeune({ titre: 'service 3' }), | ||
aServiceJeune({ titre: 'service 4' }), | ||
aServiceJeune({ titre: 'service 5' }), | ||
aServiceJeune({ titre: 'service 6' }), | ||
aServiceJeune({ titre: 'service 7' }), | ||
]; | ||
const analyticsService = aManualAnalyticsService(); | ||
|
||
// When | ||
render( | ||
<DependenciesProvider analyticsService={analyticsService}> | ||
<ServicesJeunePage serviceJeuneList={serviceJeuneList} /> | ||
</DependenciesProvider>, | ||
); | ||
|
||
// Then | ||
const mesuresJeunesSection = screen.getByRole('region', { name: 'les services jeunes' }); | ||
const voirPlusDeServicesJeunesBouton = within(mesuresJeunesSection).getByRole('button', { name: 'Voir plus de services conçus pour les jeunes' }); | ||
expect(voirPlusDeServicesJeunesBouton).toBeVisible(); | ||
}); | ||
it('affiche un bouton voir moins quand plus de 6 services jeunes sont visibles', async () => { | ||
// Given | ||
const serviceJeuneList = [ | ||
aServiceJeune({ titre: 'service 1' }), | ||
aServiceJeune({ titre: 'service 2' }), | ||
aServiceJeune({ titre: 'service 3' }), | ||
aServiceJeune({ titre: 'service 4' }), | ||
aServiceJeune({ titre: 'service 5' }), | ||
aServiceJeune({ titre: 'service 6' }), | ||
aServiceJeune({ titre: 'service 7' }), | ||
]; | ||
const analyticsService = aManualAnalyticsService(); | ||
|
||
render( | ||
<DependenciesProvider analyticsService={analyticsService}> | ||
<ServicesJeunePage serviceJeuneList={serviceJeuneList} /> | ||
</DependenciesProvider>, | ||
); | ||
const mesuresJeunesSection = screen.getByRole('region', { name: 'les services jeunes' }); | ||
const voirPlusDeServicesJeunesBouton = within(mesuresJeunesSection).getByRole('button', { name: 'Voir plus de services conçus pour les jeunes' }); | ||
|
||
// When | ||
await userEvent.click(voirPlusDeServicesJeunesBouton); | ||
|
||
// Then | ||
const voirMoinsDeServicesJeunesBouton = within(mesuresJeunesSection).getByRole('button', { name: 'Voir moins de services conçus pour les jeunes' }); | ||
expect(voirMoinsDeServicesJeunesBouton).toBeVisible(); | ||
}); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import { GetStaticPropsResult } from 'next'; | ||
import React from 'react'; | ||
|
||
import { ServicesJeunes } from '~/client/components/features/ServicesJeunes/ServicesJeunes'; | ||
import { Head } from '~/client/components/head/Head'; | ||
import { LightHero, LightHeroPrimaryText, LightHeroSecondaryText } from '~/client/components/ui/Hero/LightHero'; | ||
import useAnalytics from '~/client/hooks/useAnalytics'; | ||
import analytics from '~/pages/espace-jeune/index.analytics'; | ||
import { isFailure } from '~/server/errors/either'; | ||
import { ServiceJeune } from '~/server/services-jeunes/domain/servicesJeunes'; | ||
import { dependencies } from '~/server/start'; | ||
|
||
import styles from './index.module.scss'; | ||
|
||
interface ServicesJeunePageProps { | ||
serviceJeuneList: Array<ServiceJeune> | ||
} | ||
|
||
export default function ServicesJeunesPage({ serviceJeuneList }: ServicesJeunePageProps) { | ||
useAnalytics(analytics); | ||
|
||
return ( | ||
<> | ||
<Head | ||
title="Services jeunes | 1jeune1solution" | ||
robots="index,follow" /> | ||
<main id="contenu"> | ||
<LightHero> | ||
<h2> | ||
<LightHeroPrimaryText>Services jeunes, retrouvez les services conçus pour vous :</LightHeroPrimaryText> | ||
<LightHeroSecondaryText> | ||
entrée dans la vie professionnelle, orientation, formation, accompagnement, logement, aides et outils | ||
</LightHeroSecondaryText> | ||
</h2> | ||
</LightHero> | ||
<section className={styles.section} aria-label="les services jeunes"> | ||
<ServicesJeunes cardList={serviceJeuneList} /> | ||
</section> | ||
</main> | ||
</> | ||
); | ||
} | ||
|
||
export async function getStaticProps(): Promise<GetStaticPropsResult<ServicesJeunePageProps>> { | ||
const isServicesJeunesVisible = process.env.NEXT_PUBLIC_OLD_ESPACE_JEUNE_FEATURE === '0'; | ||
if (!isServicesJeunesVisible) { | ||
return { notFound: true }; | ||
} | ||
|
||
const serviceJeuneList = await dependencies.servicesJeunesDependencies.consulterLesServicesJeunesUseCase.handle(); | ||
|
||
if (isFailure(serviceJeuneList)) { | ||
return { notFound: true, revalidate: 1 }; | ||
} | ||
|
||
return { | ||
props: { | ||
serviceJeuneList: JSON.parse(JSON.stringify(serviceJeuneList.result)), | ||
}, | ||
revalidate: dependencies.cmsDependencies.duréeDeValiditéEnSecondes(), | ||
}; | ||
} | ||
|