Skip to content

Commit

Permalink
Mise en place du formulaire candidature d'un conseiller
Browse files Browse the repository at this point in the history
  • Loading branch information
NerOcrO committed Jun 27, 2024
1 parent e3c87d6 commit 341c08d
Show file tree
Hide file tree
Showing 10 changed files with 235 additions and 17 deletions.
30 changes: 30 additions & 0 deletions .github/workflows/continuous-integration.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: Continuous Integration

on: pull_request

jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
cache: npm
node-version-file: package.json
- name: Install modules
run: npm i
- name: Lint
run: npm run lint

tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
cache: npm
node-version-file: package.json
- name: Install modules
run: npm i
- name: Lint
run: npm run test
17 changes: 0 additions & 17 deletions .github/workflows/eslint.yml

This file was deleted.

2 changes: 2 additions & 0 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import Carte from './views/Carte';
import CarteCoordinateur from './views/coordination-territoriale/CarteCoordinateur';
import Formation from './views/formation';
import DonneesPersonnelles from './views/donnees-personnelles';
import PageCandidatureConseiller from './views/candidature-conseiller/PageCandidatureConseiller';

const RedirectCarto = () => {
const { permanence } = useParams();
Expand All @@ -37,6 +38,7 @@ function App() {
<Router>
<Routes>
<Route path="/accueil" element={<GestionHash />}/>
<Route path="/nouveau-formulaire-conseiller" element={<PageCandidatureConseiller />}/>
<Route path="/kit-communication" element={<KitCommunication />}/>
<Route path="/donnees-personnelles" element={<DonneesPersonnelles />}/>
<Route path="/mentions-legales" element={<MentionsLegales />}/>
Expand Down
11 changes: 11 additions & 0 deletions src/views/candidature-conseiller/CandidatureConseiller.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.cc-section {
border: 1px solid var(--grey-900-175);
}

.cc-titre {
color: var(--blue-france-sun-113-625);
}

.cc-obligatoire {
color: var(--error-425-625);
}
22 changes: 22 additions & 0 deletions src/views/candidature-conseiller/CandidatureConseiller.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from 'react';
import Sommaire from './Sommaire';
import InformationsDeContact from './InformationsDeContact';
import './CandidatureConseiller.css';

export default function CandidatureConseiller() {
return (
<div className="fr-container fr-mt-5w fr-mb-5w">
<div className="fr-grid-row">
<div className="fr-col-4">
<Sommaire />
</div>
<div className="fr-col-7">
<h1 className="cc-titre fr-mb-5w">Je veux devenir conseiller numérique</h1>
<form aria-label="Candidature conseiller">
<InformationsDeContact />
</form>
</div>
</div>
</div>
);
}
67 changes: 67 additions & 0 deletions src/views/candidature-conseiller/CandidatureConseiller.test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { render, screen, within } from '@testing-library/react';
import { describe, expect, it } from 'vitest';
import CandidatureConseiller from './CandidatureConseiller';

export const textMatcher = wording => (_, element) => {
return element?.textContent === wording;
};

describe('candidature conseiller', () => {
describe('étant un candidat', () => {
it('quand j’affiche le formulaire alors le titre et le menu s’affiche', () => {
// WHEN
render(<CandidatureConseiller />);

// THEN
const titre = screen.getByRole('heading', { level: 1, name: 'Je veux devenir conseiller numérique' });
expect(titre).toBeInTheDocument();

const navigation = screen.getByRole('navigation', { name: 'Sommaire' });
const menu = within(navigation).getByRole('list');
const menuItems = within(menu).getAllByRole('listitem');

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

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

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

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

it('quand j’affiche le formulaire alors l’étape "Vos informations de contact" est affiché', () => {
// WHEN
render(<CandidatureConseiller />);

// 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');

const champsObligatoires = within(etapeInformationsDeContact).getByText(textMatcher('Les champs avec * sont obligatoires.'), { selector: 'p' });
expect(champsObligatoires).toBeInTheDocument();

const prenom = within(etapeInformationsDeContact).getByLabelText('Prénom *');
expect(prenom).toHaveAttribute('type', 'text');
expect(prenom).toBeRequired();

const nom = within(etapeInformationsDeContact).getByLabelText('Nom *');
expect(nom).toHaveAttribute('type', 'text');
expect(nom).toBeRequired();

const email = within(etapeInformationsDeContact).getByLabelText('Adresse e-mail * Format attendu : [email protected]');
expect(email).toHaveAttribute('type', 'email');
expect(email).toBeRequired();

const telephone = within(etapeInformationsDeContact).getByLabelText('Téléphone Format attendu : 0122334455');
expect(telephone).toHaveAttribute('type', 'text');

const habitation = within(etapeInformationsDeContact).getByLabelText('Votre lieu d’habitation Saississez le nom ou le code postal de votre commune.');
expect(habitation).toHaveAttribute('type', 'text');
});
});
40 changes: 40 additions & 0 deletions src/views/candidature-conseiller/InformationsDeContact.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React from 'react';
import Input from './Input';

export default function InformationsDeContact() {
return (
<fieldset className="fr-border cc-section fr-p-3w" id="informationsDeContact">
<legend className="fr-h5">Vos informations de contact</legend>
<p className="fr-text--sm fr-hint-text">Les champs avec <span className="cc-obligatoire">*</span> sont obligatoires.</p>
<hr />
<Input
id="prenom"
>
Prénom <span className="cc-obligatoire">*</span>
</Input>
<Input
id="nom"
>
Nom <span className="cc-obligatoire">*</span>
</Input>
<Input
id="email"
type="email"
>
Adresse e-mail <span className="cc-obligatoire">*</span> <span className="fr-hint-text">Format attendu : [email protected]</span>
</Input>
<Input
id="telephone"
isRequired={false}
>
Téléphone <span className="fr-hint-text">Format attendu : 0122334455</span>
</Input>
<Input
id="lieuHabitation"
isRequired={false}
>
Votre lieu d’habitation <span className="fr-hint-text">Saississez le nom ou le code postal de votre commune.</span>
</Input>
</fieldset>
);
}
21 changes: 21 additions & 0 deletions src/views/candidature-conseiller/Input.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from 'react';
import PropTypes from 'prop-types';
import './CandidatureConseiller.css';

export default function Input({ children, id, isRequired = true, type = 'text' }) {
return (
<div className="fr-fieldset__element">
<div className="fr-input-group">
<label className="fr-label" htmlFor={id}>{children}</label>
<input className="fr-input" type={type} id={id} required={isRequired} />
</div>
</div>
);
}

Input.propTypes = {
children: PropTypes.node,
id: PropTypes.string,
isRequired: PropTypes.bool,
type: PropTypes.string,
};
12 changes: 12 additions & 0 deletions src/views/candidature-conseiller/PageCandidatureConseiller.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React from 'react';
import Header from '../../components/Header';
import CandidatureConseiller from './CandidatureConseiller';

export default function PageCandidatureConseiller() {
return (
<>
<Header />
<CandidatureConseiller />
</>
);
}
30 changes: 30 additions & 0 deletions src/views/candidature-conseiller/Sommaire.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React from 'react';

export default function Sommaire() {
return (
<nav aria-label="Sommaire" className="fr-sidemenu fr-mt-15w">
<ol className="fr-sidemenu__list">
<li className="fr-sidemenu__item fr-sidemenu__item--active">
<a href="#informationsDeContact" className="fr-sidemenu__link">
Vos informations de contact
</a>
</li>
<li className="fr-sidemenu__item fr-sidemenu__item--active">
<a href="#situtationEtExperience" className="fr-sidemenu__link">
Votre situation et expérience
</a>
</li>
<li className="fr-sidemenu__item fr-sidemenu__item--active">
<a href="#votreDisponibilite" className="fr-sidemenu__link">
Votre disponibilité
</a>
</li>
<li className="fr-sidemenu__item fr-sidemenu__item--active">
<a href="#votreDisponibilite" className="fr-sidemenu__link">
Votre motivation
</a>
</li>
</ol>
</nav>
);
}

0 comments on commit 341c08d

Please sign in to comment.