Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Creation formulaire candidature structure #196

Merged
merged 33 commits into from
Aug 29, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
19cd8e3
:feat intialisation des tests formulaire candidature structure
dienamo Jul 22, 2024
7b26124
test formulaire partie 'information contact' + 'votre besoin CN'
Ornella452 Jul 25, 2024
a0df62d
Retours de code review
Alezco Jul 29, 2024
3bab5dd
Ajout d'un scroll vers la bonne section du formulaire
Alezco Jul 29, 2024
336cde6
:feat ajout premier bloc formulaire informations contact structure
dienamo Jul 30, 2024
46f7a3e
Refacto des informations de structure
Alezco Jul 30, 2024
4e7e725
partie Votre motivation + test
Ornella452 Jul 30, 2024
088b8d8
Merge branch 'creation-formulaire-candidature-structure' of https://g…
Ornella452 Jul 30, 2024
c9955e1
:feat ajout premier bloc formulaire informations contact structure
dienamo Jul 30, 2024
9c59059
Refacto des informations de structure
Alezco Jul 30, 2024
38d30e0
Retours de code review
Alezco Jul 30, 2024
7664c55
partie engagement + test
Ornella452 Jul 31, 2024
246b437
partie submit + test + rectif lint
Ornella452 Jul 31, 2024
d238e91
resolve conflict
Ornella452 Jul 31, 2024
7155a8b
Merge branch 'recette' of https://github.com/anct-cnum/site-vitrine i…
Ornella452 Jul 31, 2024
6b34c80
:feat intialisation des tests formulaire candidature structure
dienamo Jul 22, 2024
7f3a562
test formulaire partie 'information contact' + 'votre besoin CN'
Ornella452 Jul 25, 2024
ef5738e
Retours de code review
Alezco Jul 29, 2024
897c932
Ajout d'un scroll vers la bonne section du formulaire
Alezco Jul 29, 2024
d64b562
partie Votre motivation + test
Ornella452 Jul 30, 2024
17cd1c2
:feat ajout premier bloc formulaire informations contact structure
dienamo Jul 30, 2024
dddc38b
Refacto des informations de structure
Alezco Jul 30, 2024
9198df2
partie engagement + test
Ornella452 Jul 31, 2024
e9ef420
partie submit + test + rectif lint
Ornella452 Jul 31, 2024
aff9161
:feat ajout premier bloc formulaire informations contact structure
dienamo Jul 30, 2024
527ae03
Refacto des informations de structure
Alezco Jul 30, 2024
049e4f8
Retours de code review
Alezco Jul 30, 2024
dd0017f
Correction des tests
Alezco Aug 1, 2024
9247550
quelques rectif required + config min + ponctuation
Ornella452 Aug 1, 2024
6fff0a7
Merge branch 'creation-formulaire-candidature-structure' of https://g…
Ornella452 Aug 1, 2024
37a7a84
retour commentaire , required pour la checkbox
Ornella452 Aug 1, 2024
ed0d2be
:feat encart information structure
dienamo Aug 5, 2024
e63ed7a
:fix refacto nommage retours commentaires
dienamo Aug 8, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ function App() {
const CoordinationTerritoriale = lazy(() => import('./views/coordination-territoriale'));
const CarteCoordinateur = lazy(() => import('./views/coordination-territoriale/CarteCoordinateur'));
const PageCandidatureConseiller = lazy(() => import('./views/candidature-conseiller/PageCandidatureConseiller'));
const PageCandidatureStructure = lazy(() => import('./views/candidature-structure/PageCandidatureStructure'));

return (
<div className="App">
Expand All @@ -45,6 +46,7 @@ function App() {
<Routes>
<Route path="/accueil" element={<GestionHash />}/>
<Route path="/nouveau-formulaire-conseiller" element={<PageCandidatureConseiller />}/>
<Route path="/nouveau-formulaire-structure" element={<PageCandidatureStructure />}/>
<Route path="/kit-communication" element={<KitCommunication />}/>
<Route path="/donnees-personnelles" element={<DonneesPersonnelles />}/>
<Route path="/mentions-legales" element={<MentionsLegales />}/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,11 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { useLocation } from 'react-router-dom';

export default function Sommaire() {
const ancreInformationsDeContact = '#informationsDeContact';
const [dernierElementClique, setDernierElementClique] = useState(ancreInformationsDeContact);
export default function Sommaire({ parties }) {
const { hash } = useLocation();

const partiesSommaire = [
{
ancre: ancreInformationsDeContact,
libelle: 'Vos informations de contact'
},
{
ancre: '#situationEtExperience',
libelle: 'Votre situation et expérience'
},
{
ancre: '#votreDisponibilite',
libelle: 'Votre disponibilité'
},
{
ancre: '#votreMotivation',
libelle: 'Votre motivation'
},
];
const [dernierElementClique, setDernierElementClique] = useState(hash || parties[0].ancre);

const getAriaCurrent = ancre => {
return ancre === dernierElementClique ? 'page' : false;
Expand All @@ -30,7 +14,7 @@ export default function Sommaire() {
return (
<nav aria-label="Sommaire" className="fr-sidemenu fr-mt-15w fr-sidemenu--sticky-full-height">
<ol className="fr-sidemenu__list">
{partiesSommaire.map(({ ancre, libelle }, index) => (
{parties.map(({ ancre, libelle }, index) => (
<li className="fr-sidemenu__item" key={index} onClick={() => setDernierElementClique(ancre)}>
<a href={ancre} className="fr-sidemenu__link" target="_self" aria-current={getAriaCurrent(ancre)}>
{libelle}
Expand All @@ -41,3 +25,7 @@ export default function Sommaire() {
</nav >
);
}

Sommaire.propTypes = {
parties: PropTypes.array,
};
13 changes: 13 additions & 0 deletions src/hooks/useScrollToSection.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';

export const useScrollToSection = () => {
const { hash } = useLocation();

useEffect(() => {
const sectionId = hash.split('#')[1];
if (sectionId) {
document.getElementById(sectionId).scrollIntoView();
}
}, []);
};
7 changes: 5 additions & 2 deletions src/views/candidature-conseiller/CandidatureConseiller.jsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import React, { useState } from 'react';
import Sommaire from './Sommaire';
import SommaireConseiller from './SommaireConseiller';
import InformationsDeContact from './InformationsDeContact';
import SituationEtExperience from './SituationEtExperience';
import Disponibilite from './Disponibilite';
import Motivation from './Motivation';
import EnResume from './EnResume';
import { situations } from './situations';
import { useScrollToSection } from '../../hooks/useScrollToSection';

import '@gouvfr/dsfr/dist/component/form/form.min.css';
import '@gouvfr/dsfr/dist/component/input/input.min.css';
Expand All @@ -23,6 +24,8 @@ export default function CandidatureConseiller() {
new Array(situations.length).fill(false)
);

useScrollToSection();

const valider = () => {
setIsSituationValid(situationChecks.some(checked => checked));
if (!isSituationValid) {
Expand All @@ -34,7 +37,7 @@ export default function CandidatureConseiller() {
<div className="fr-container fr-mt-5w fr-mb-5w">
<div className="fr-grid-row">
<div className="fr-col-12 fr-col-md-4">
<Sommaire />
<SommaireConseiller />
</div>
<div className="fr-col-12 fr-col-md-8 fr-py-12v">
<h1 className="cc-titre fr-mb-5w">Je veux devenir conseiller numérique</h1>
Expand Down
24 changes: 14 additions & 10 deletions src/views/candidature-conseiller/CandidatureConseiller.test.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { render, screen, within, fireEvent } from '@testing-library/react';
import { describe, expect, it, vi } from 'vitest';
import CandidatureConseiller from './CandidatureConseiller';
import { textMatcher } from './test-utils';
import { textMatcher } from '../../../test/test-utils';

vi.mock('react-router-dom', () => ({
useLocation: () => ({ hash: '' }),
}));

describe('candidature conseiller', () => {
describe('étant un candidat', () => {
Expand All @@ -21,16 +25,16 @@ describe('candidature conseiller', () => {
const menuItems = within(menu).getAllByRole('listitem');

const informationsDeContact = within(menuItems[0]).getByRole('link', { name: 'Vos informations de contact' });
expect(informationsDeContact).toHaveAttribute('href', '#informationsDeContact');
expect(informationsDeContact).toHaveAttribute('href', '#informations-de-contact');

const situationEtExperience = within(menuItems[1]).getByRole('link', { name: 'Votre situation et expérience' });
expect(situationEtExperience).toHaveAttribute('href', '#situationEtExperience');
expect(situationEtExperience).toHaveAttribute('href', '#situation-et-experience');

const votreDisponibilite = within(menuItems[2]).getByRole('link', { name: 'Votre disponibilité' });
expect(votreDisponibilite).toHaveAttribute('href', '#votreDisponibilite');
expect(votreDisponibilite).toHaveAttribute('href', '#votre-disponibilite');

const votreMotivation = within(menuItems[3]).getByRole('link', { name: 'Votre motivation' });
expect(votreMotivation).toHaveAttribute('href', '#votreMotivation');
expect(votreMotivation).toHaveAttribute('href', '#votre-motivation');
});
});

Expand All @@ -41,7 +45,7 @@ describe('candidature conseiller', () => {
// THEN
const formulaire = screen.getByRole('form', { name: 'Candidature conseiller' });
const etapeInformationsDeContact = within(formulaire).getByRole('group', { name: 'Vos informations de contact' });
expect(etapeInformationsDeContact).toHaveAttribute('id', 'informationsDeContact');
expect(etapeInformationsDeContact).toHaveAttribute('id', 'informations-de-contact');

const prenom = within(etapeInformationsDeContact).getByLabelText('Prénom *');
expect(prenom).toHaveAttribute('type', 'text');
Expand Down Expand Up @@ -70,7 +74,7 @@ describe('candidature conseiller', () => {
// THEN
const formulaire = screen.getByRole('form', { name: 'Candidature conseiller' });
const situationEtExperience = within(formulaire).getByRole('group', { name: 'Votre situation et expérience' });
expect(situationEtExperience).toHaveAttribute('id', 'situationEtExperience');
expect(situationEtExperience).toHaveAttribute('id', 'situation-et-experience');

const situation = within(situationEtExperience).getByText(textMatcher('Êtes-vous actuellement dans l’une des situations suivantes ? *'), { selector: 'p' });
expect(situation).toBeInTheDocument();
Expand Down Expand Up @@ -131,7 +135,7 @@ describe('candidature conseiller', () => {
// THEN
const formulaire = screen.getByRole('form', { name: 'Candidature conseiller' });
const votreDisponibilite = within(formulaire).getByRole('group', { name: 'Votre disponibilité' });
expect(votreDisponibilite).toHaveAttribute('id', 'votreDisponibilite');
expect(votreDisponibilite).toHaveAttribute('id', 'votre-disponibilite');

const questionDisponibilite = within(votreDisponibilite).getByText(
textMatcher('À quel moment êtes-vous prêt(e) à démarrer votre mission et la formation de conseiller numérique ? *'),
Expand Down Expand Up @@ -197,7 +201,7 @@ describe('candidature conseiller', () => {
// THEN
const formulaire = screen.getByRole('form', { name: 'Candidature conseiller' });
const votreMotivation = within(formulaire).getByRole('group', { name: 'Votre motivation' });
expect(votreMotivation).toHaveAttribute('id', 'votreMotivation');
expect(votreMotivation).toHaveAttribute('id', 'votre-motivation');

const aideMotivation = within(votreMotivation).getByText(
textMatcher('En quelques lignes, décrivez votre motivation personnelle pour devenir conseiller numérique ' +
Expand Down Expand Up @@ -234,7 +238,7 @@ describe('candidature conseiller', () => {
expect(descriptionResume).toBeInTheDocument();
});

it('quand je modifie la date de disponibilité, elle s’affiche dans l’encart "En résumé" est affiché', () => {
it('quand je modifie la date de disponibilité, alors elle s’affiche dans l’encart "En résumé"', () => {
// GIVEN
render(<CandidatureConseiller />);
const dateDisponibilite = '2023-12-12';
Expand Down
2 changes: 1 addition & 1 deletion src/views/candidature-conseiller/Disponibilite.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import PropTypes from 'prop-types';

export default function Disponibilite({ setDateDisponibilite }) {
return (
<fieldset className="fr-border cc-section fr-p-3w fr-mb-3w" id="votreDisponibilite">
<fieldset className="fr-border cc-section fr-p-3w fr-mb-3w" id="votre-disponibilite">
<legend className="fr-h5">Votre disponibilité</legend>
<hr />
<p className="fr-mb-3w cc-bold">
Expand Down
2 changes: 1 addition & 1 deletion src/views/candidature-conseiller/InformationsDeContact.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import AddressChooser from './AddressChooser';

export default function InformationsDeContact() {
return (
<fieldset className="fr-border cc-section fr-p-3w fr-mb-3w" id="informationsDeContact">
<fieldset className="fr-border cc-section fr-p-3w fr-mb-3w" id="informations-de-contact">
<legend className="fr-h5">Vos informations de contact</legend>
<hr />
<Input
Expand Down
2 changes: 1 addition & 1 deletion src/views/candidature-conseiller/Motivation.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import ZoneDeTexte from '../../components/commun/ZoneDeTexte';

export default function Motivation() {
return (
<fieldset className="fr-border cc-section fr-p-3w fr-mb-3w" id="votreMotivation">
<fieldset className="fr-border cc-section fr-p-3w fr-mb-3w" id="votre-motivation">
<legend className="fr-h5" id="titreMotivation">Votre motivation</legend>
<p className="fr-text--sm fr-hint-text">
En quelques lignes, décrivez votre motivation personnelle pour devenir conseiller numérique et{' '}
Expand Down
2 changes: 1 addition & 1 deletion src/views/candidature-conseiller/SituationEtExperience.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export default function SituationEtExperience({ situationChecks, setSituationChe
};

return (
<fieldset className="fr-border cc-section fr-p-3w fr-mb-3w" id="situationEtExperience">
<fieldset className="fr-border cc-section fr-p-3w fr-mb-3w" id="situation-et-experience">
<legend className="fr-h5">Votre situation et expérience</legend>
<hr />
<p className="fr-mb-3w cc-bold">
Expand Down
27 changes: 27 additions & 0 deletions src/views/candidature-conseiller/SommaireConseiller.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React from 'react';
import Sommaire from '../../components/Sommaire';

export default function SommaireConseiller() {
const parties = [
{
ancre: '#informations-de-contact',
libelle: 'Vos informations de contact'
},
{
ancre: '#situation-et-experience',
libelle: 'Votre situation et expérience'
},
{
ancre: '#votre-disponibilite',
libelle: 'Votre disponibilité'
},
{
ancre: '#votre-motivation',
libelle: 'Votre motivation'
},
];

return (
<Sommaire parties={parties} />
);
}
36 changes: 36 additions & 0 deletions src/views/candidature-structure/BesoinEnConseillerNumerique.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React from 'react';

import BoutonRadio from '../../components/commun/BoutonRadio';
import Datepicker from '../../components/commun/Datepicker';
import Input from '../../components/commun/Input';
import PropTypes from 'prop-types';

export default function BesoinEnConseillerNumerique({ setDateAccueilConseillerNumerique }) {
return (
<fieldset className="fr-border cc-section fr-p-3w fr-mb-3w" id="votre-besoin-en-conseiller-numerique">
<legend className="fr-h5">Votre besoin en conseiller(s) numérique(s)</legend>
<hr />
<Input id="nombreConseillersSouhaites" type="number">
Combien de conseillers numériques souhaitez-vous accueillir ?<span className="cc-obligatoire">*</span>
</Input>
<hr />
<p className="fr-mb-3w cc-bold">Avez-vous déjà identifié un candidat pour le poste de conseiller numérique ?<span className="cc-obligatoire">*</span></p>
<p className="fr-text--sm fr-hint-text">Si oui, merci d’inviter ce candidat à s’inscrire sur la plateforme Conseiller numérique</p>
<BoutonRadio id="oui" nomGroupe="identificationCandidat">
Oui
</BoutonRadio>
<BoutonRadio id="non" nomGroupe="identificationCandidat">
Non
</BoutonRadio>
<hr />
<p className="fr-mb-3w cc-bold">À partir de quand êtes vous prêt à accueillir votre conseiller numerique ?<span className="cc-obligatoire">*</span></p>
<Datepicker id="choisir-date" onChange={event => setDateAccueilConseillerNumerique(event.target.value)}>
Choisir une date
</Datepicker>
</fieldset >
);
}

BesoinEnConseillerNumerique.propTypes = {
setDateAccueilConseillerNumerique: PropTypes.func,
};
39 changes: 39 additions & 0 deletions src/views/candidature-structure/CandidatureStructure.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React, { useState } from 'react';
import SommaireStructure from './SommaireStructure';
import InformationsDeContact from './InformationsDeContact';
import BesoinEnConseillerNumerique from './BesoinEnConseillerNumerique';
import { useScrollToSection } from '../../hooks/useScrollToSection';

import '@gouvfr/dsfr/dist/component/form/form.min.css';
import '@gouvfr/dsfr/dist/component/input/input.min.css';
import '@gouvfr/dsfr/dist/component/checkbox/checkbox.min.css';
import '@gouvfr/dsfr/dist/component/radio/radio.min.css';
import '@gouvfr/dsfr/dist/component/badge/badge.min.css';
import '@gouvfr/dsfr/dist/component/notice/notice.min.css';
import '@gouvfr/dsfr/dist/component/sidemenu/sidemenu.min.css';
import '../candidature-conseiller/CandidatureConseiller.css';

export default function CandidatureStructure() {
const [dateAccueilConseillerNumerique, setDateAccueilConseillerNumerique] = useState();

useScrollToSection();

return (
<div className="fr-container fr-mt-5w fr-mb-5w">
<div className="fr-grid-row">
<div className="fr-col-12 fr-col-md-4">
<SommaireStructure />
</div>
<div className="fr-col-12 fr-col-md-8 fr-py-12v">
<h1 className="cc-titre fr-mb-5w">Je souhaite engager un conseiller numérique</h1>
<p className="fr-text--sm fr-hint-text">Les champs avec <span className="cc-obligatoire">*</span> sont obligatoires.</p>
<form aria-label="Candidature structure" >
{/* TODO : Vos informations de structure */}
<InformationsDeContact />
<BesoinEnConseillerNumerique setDateAccueilConseillerNumerique={setDateAccueilConseillerNumerique} />
</form>
</div>
</div>
</div>
);
}
Loading
Loading