Merge pull request #4762 from betagouv/form-stylesheet

Usager : amélioration de la clarté et de l'accessibilité des formulaires
This commit is contained in:
Pierre de La Morinerie 2020-02-11 17:38:26 +01:00 committed by GitHub
commit d5fcaf7073
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 152 additions and 66 deletions

View file

@ -3,6 +3,7 @@ $small-page-width: 840px;
$default-spacer: 8px; $default-spacer: 8px;
$default-padding: 2 * $default-spacer; $default-padding: 2 * $default-spacer;
$default-fields-spacer: 5 * $default-spacer;
// layouts // layouts
$two-columns-padding: 60px; $two-columns-padding: 60px;

View file

@ -20,7 +20,7 @@
} }
%outline { %outline {
&:active, &:active:not(:disabled),
&:focus { &:focus {
outline: 3px solid $blue; outline: 3px solid $blue;
} }

View file

@ -16,13 +16,13 @@
width: 100%; width: 100%;
height: 0; height: 0;
margin-top: $default-padding; margin-top: $default-padding;
margin-bottom: 2 * $default-padding; margin-bottom: $default-fields-spacer;
border: none; border: none;
border-bottom: 1px solid $border-grey; border-bottom: 2px solid $border-grey;
} }
@mixin notice-text-style { @mixin notice-text-style {
font-size: 14px; font-size: 16px;
color: $grey; color: $grey;
} }
@ -31,6 +31,7 @@
} }
label { label {
font-size: 18px;
margin-bottom: $default-padding; margin-bottom: $default-padding;
display: block; display: block;
font-weight: bold; font-weight: bold;
@ -45,7 +46,6 @@
.notice { .notice {
@include notice-text-style; @include notice-text-style;
font-weight: bold;
margin-top: - $default-spacer; margin-top: - $default-spacer;
margin-bottom: $default-padding; margin-bottom: $default-padding;
@ -70,32 +70,80 @@
visibility: visible; visibility: visible;
} }
// Align checkboxes on the top-left side of the label // Move checkbox to the top-left side of the label
&.editable-champ-checkbox, &.editable-champ-checkbox,
&.editable-champ-radio.vertical,
&.editable-champ-engagement { &.editable-champ-engagement {
p, p,
label { label {
padding-left: 28px; padding-left: 28px;
} }
input[type=checkbox], input[type=checkbox] {
input[type=radio] {
position: absolute; position: absolute;
top: 1px; top: 3px;
left: 0px; left: 0px;
} }
// When an (eventual) notice is displayed after the input, give it some bottom margin.
.notice {
margin-bottom: $default-fields-spacer;
}
} }
} }
.radios { .radios {
display: flex;
// Horizontal layout (default)
flex-direction: row;
align-items: flex-start;
label { label {
display: inline; margin-right: $default-padding;
margin-left: $default-padding; }
// Vertical layout
&.vertical {
flex-direction: column;
align-items: stretch;
label {
margin-right: 0;
}
}
label {
padding: $default-padding $default-padding $default-padding $default-spacer;
border: 1px solid $border-grey;
border-radius: 4px;
font-weight: normal;
background: $white;
user-select: none;
&:last-of-type {
margin-bottom: $default-fields-spacer;
}
&:hover {
background: $light-grey;
cursor: pointer;
}
&:active {
border-color: darken($border-grey, 10);
}
&:first-child { &:first-child {
margin-left: 0; margin-left: 0;
} }
input[type=radio] {
margin-bottom: 0;
}
.notice {
margin: 4px 0 0 27px;
}
} }
} }
@ -109,7 +157,7 @@
select, select,
.attachment { .attachment {
display: block; display: block;
margin-bottom: 2 * $default-padding; margin-bottom: $default-fields-spacer;
&.small-margin { &.small-margin {
margin-bottom: $default-padding / 2; margin-bottom: $default-padding / 2;
@ -117,7 +165,7 @@
} }
.add-row { .add-row {
margin-bottom: 2 * $default-padding; margin-bottom: $default-fields-spacer;
} }
input[type=checkbox] { input[type=checkbox] {
@ -182,16 +230,14 @@
input[type=radio] { input[type=radio] {
@extend %outline; @extend %outline;
margin-left: 5px; // Firefox tends to display some controls smaller than other browsers.
margin-right: 4px;
margin-bottom: 2 * $default-padding;
}
input[type=checkbox] {
// Firefox tends to display checkbox controls smaller than other browsers.
// Ensure a consistency of size between browsers. // Ensure a consistency of size between browsers.
width: 16px; width: 16px;
height: 16px; height: 16px;
margin-left: 5px;
margin-right: 4px;
margin-bottom: $default-fields-spacer;
} }
input[type=date] { input[type=date] {
@ -234,7 +280,7 @@
.select2-container { .select2-container {
display: block; display: block;
margin-bottom: 2 * $default-padding; margin-bottom: $default-fields-spacer;
&.select2-container--focus { &.select2-container--focus {
.select2-selection { .select2-selection {
@ -304,10 +350,17 @@
} }
.header-section { .header-section {
display: inline-block;
color: $blue; color: $blue;
font-weight: bold; font-size: 30px;
font-size: 20px; margin-bottom: 3 * $default-padding;
margin-bottom: 2 * $default-padding; border-bottom: 3px solid $blue;
}
.header-subsection {
font-size: 22px;
color: $blue;
margin-bottom: $default-padding;
} }
.explication-libelle { .explication-libelle {
@ -317,7 +370,7 @@
} }
.explication { .explication {
margin-bottom: $default-padding; margin-bottom: $default-fields-spacer;
padding: $default-padding / 2; padding: $default-padding / 2;
background-color: $light-grey; background-color: $light-grey;
@ -326,6 +379,13 @@
} }
} }
.siret-info {
margin-top: -$default-fields-spacer;
margin-bottom: $default-fields-spacer;
// Ensure the bottom-margin is not collapsed when the element is empty
min-height: 1px;
}
.send-wrapper { .send-wrapper {
display: flex; display: flex;
width: 100%; width: 100%;
@ -388,15 +448,4 @@
margin-right: 0; margin-right: 0;
} }
} }
.pj-input {
input[type=file] {
margin: $default-padding 0 (2 * $default-padding);
padding: 2px;
}
.piece-description {
margin-bottom: $default-padding;
}
}
} }

View file

@ -2,4 +2,11 @@ class Champs::HeaderSectionChamp < Champ
def search_terms def search_terms
# The user cannot enter any information here so it doesnt make much sense to search # The user cannot enter any information here so it doesnt make much sense to search
end end
def section_index
dossier
.champs
.filter { |c| c.type_champ == TypeDeChamp.type_champs.fetch(:header_section) }
.index(self) + 1
end
end end

View file

@ -7,5 +7,6 @@
= render partial: "shared/dossiers/editable_champs/editable_champ", locals: { champ: champ, form: form } = render partial: "shared/dossiers/editable_champs/editable_champ", locals: { champ: champ, form: form }
= form.hidden_field :id = form.hidden_field :id
= form.hidden_field :_destroy, disabled: true = form.hidden_field :_destroy, disabled: true
%button.button.danger.remove-row .flex.row-reverse
Supprimer %button.button.danger.remove-row
Supprimer lélément

View file

@ -78,18 +78,17 @@
- if !@procedure.locked? - if !@procedure.locked?
%h2.header-section À qui sadresse ma démarche ? %h2.header-section À qui sadresse ma démarche ?
.editable-champ.editable-champ-radio.vertical .radios.vertical
= f.label :for_individual, value: true do = f.label :for_individual, value: true do
= f.radio_button :for_individual, true
Ma démarche sadresse à un particulier Ma démarche sadresse à un particulier
%p.notice En choisissant cette option, lusager devra renseigner son nom et prénom avant daccéder au formulaire %p.notice En choisissant cette option, lusager devra renseigner son nom et prénom avant daccéder au formulaire
= f.radio_button :for_individual, true
.editable-champ.editable-champ-radio.vertical
= f.label :for_individual, value: false do = f.label :for_individual, value: false do
= f.radio_button :for_individual, false
Ma démarche sadresse à une personne morale Ma démarche sadresse à une personne morale
%p.notice %p.notice
En choisissant cette option, lusager devra renseigner son n° SIRET.<br>Grâce à lAPI Entreprise, les informations sur la personne morale (raison sociale, adresse du siège, etc.) seront automatiquement renseignées. En choisissant cette option, lusager devra renseigner son n° SIRET.<br>Grâce à lAPI Entreprise, les informations sur la personne morale (raison sociale, adresse du siège, etc.) seront automatiquement renseignées.
= f.radio_button :for_individual, false
%p.explication %p.explication
Si votre démarche sadresse indifféremment à une personne morale ou un particulier, choisissez l'option « Particuliers ». Vous pourrez ajouter un champ SIRET directement dans le formulaire. Si votre démarche sadresse indifféremment à une personne morale ou un particulier, choisissez l'option « Particuliers ». Vous pourrez ajouter un champ SIRET directement dans le formulaire.

View file

@ -52,16 +52,16 @@
%label Mot de passe %label Mot de passe
%input{ type: "password", value: "12345678" } %input{ type: "password", value: "12345678" }
%h2.header-section Bouton radio verticaux %h3.header-subsection Bouton radio verticaux
.editable-champ.editable-champ-radio.vertical .radios.vertical
= f.label :archived, 'Option A', value: true = f.label :archived, value: true do
%p.notice Une option tout à fait valable. = f.radio_button :archived, true
= f.radio_button :archived, true Option A
%p.notice Une option tout à fait valable.
.editable-champ.editable-champ-radio.vertical = f.label :archived, value: false do
= f.label :archived, 'Option B', value: false = f.radio_button :archived, false
%p.notice Une autre option, pas mal non plus. Option B
= f.radio_button :archived, false %p.notice Une autre option, pas mal non plus.
.send-wrapper .send-wrapper
= f.submit 'Enregistrer un brouillon (formnovalidate)', formnovalidate: true, class: 'button send' = f.submit 'Enregistrer un brouillon (formnovalidate)', formnovalidate: true, class: 'button send'

View file

@ -1,9 +1,9 @@
.editable-champ{ class: "editable-champ-#{champ.type_champ}" } .editable-champ{ class: "editable-champ-#{champ.type_champ}" }
- if champ.repetition? - if champ.repetition?
= render partial: 'shared/dossiers/editable_champs/header_section', locals: { champ: champ } %h3.header-subsection= champ.libelle
- if champ.description.present? - if champ.description.present?
%p.notice= string_to_html(champ.description, false) %p.notice= string_to_html(champ.description, false)
- elsif has_label?(champ) - elsif has_label?(champ)
= render partial: 'shared/dossiers/editable_champs/champ_label', locals: { form: form, champ: champ, seen_at: defined?(seen_at) ? seen_at : nil } = render partial: 'shared/dossiers/editable_champs/champ_label', locals: { form: form, champ: champ, seen_at: defined?(seen_at) ? seen_at : nil }

View file

@ -1,2 +1,6 @@
- section_index = champ.section_index
%h2.header-section %h2.header-section
- if section_index
= "#{section_index}."
= champ.libelle = champ.libelle

View file

@ -8,16 +8,16 @@
.flex.row-reverse .flex.row-reverse
- if champ.persisted? - if champ.persisted?
%button.button.danger.remove-row{ type: :button } %button.button.danger.remove-row{ type: :button }
Supprimer Supprimer lélément
- else - else
%button.button.danger{ type: :button } %button.button.danger{ type: :button }
Supprimer Supprimer lélément
- if champ.persisted? - if champ.persisted?
= link_to champs_repetition_path(form.index), class: 'button add-row', data: { remote: true, method: 'POST', params: { champ_id: champ&.id }.to_query } do = link_to champs_repetition_path(form.index), class: 'button add-row', data: { remote: true, method: 'POST', params: { champ_id: champ&.id }.to_query } do
%span.icon.add %span.icon.add
Ajouter une ligne pour « #{champ.libelle} » Ajouter un élément pour « #{champ.libelle} »
- else - else
%a.button.add-row %a.button.add-row
%span.icon.add %span.icon.add
Ajouter une ligne pour « #{champ.libelle} » Ajouter un élément pour « #{champ.libelle} »

View file

@ -1,11 +1,10 @@
= form.text_field :value, = form.text_field :value,
placeholder: champ.libelle, placeholder: champ.libelle,
class: 'small-margin',
data: { remote: true, debounce: true, url: champs_siret_path(form.index), params: { champ_id: champ&.id }.to_query, spinner: true }, data: { remote: true, debounce: true, url: champs_siret_path(form.index), params: { champ_id: champ&.id }.to_query, spinner: true },
required: champ.mandatory?, required: champ.mandatory?,
pattern: "[0-9]{14}", pattern: "[0-9]{14}",
title: "Le numéro de SIRET doit comporter exactement 14 chiffres" title: "Le numéro de SIRET doit comporter exactement 14 chiffres"
.spinner.right.hidden .spinner.right.hidden
%div{ class: "siret-info-#{form.index}" } .siret-info{ class: "siret-info-#{form.index}" }
- if champ.etablissement.present? - if champ.etablissement.present?
= render partial: 'shared/dossiers/editable_champs/etablissement_titre', locals: { etablissement: champ.etablissement } = render partial: 'shared/dossiers/editable_champs/etablissement_titre', locals: { etablissement: champ.etablissement }

View file

@ -107,7 +107,7 @@ feature 'The user' do
fill_in('text', with: 'super texte') fill_in('text', with: 'super texte')
expect(page).to have_field('text', with: 'super texte') expect(page).to have_field('text', with: 'super texte')
click_on 'Ajouter une ligne pour' click_on 'Ajouter un élément pour'
within '.row-1' do within '.row-1' do
fill_in('text', with: 'un autre texte') fill_in('text', with: 'un autre texte')
@ -120,7 +120,7 @@ feature 'The user' do
expect(page).to have_content('Supprimer', count: 2) expect(page).to have_content('Supprimer', count: 2)
within '.row-1' do within '.row-1' do
click_on 'Supprimer' click_on 'Supprimer lélément'
end end
click_on 'Enregistrer le brouillon' click_on 'Enregistrer le brouillon'

View file

@ -0,0 +1,26 @@
require 'spec_helper'
describe Champs::CheckboxChamp do
let(:types_de_champ) do
[
create(:type_de_champ_header_section),
create(:type_de_champ_civilite),
create(:type_de_champ_text),
create(:type_de_champ_header_section),
create(:type_de_champ_email)
]
end
let(:procedure) { create(:procedure, types_de_champ: types_de_champ) }
let(:dossier) { create(:dossier, procedure: procedure) }
describe '#section_index' do
let(:first_header) { dossier.champs[0] }
let(:second_header) { dossier.champs[3] }
it 'returns the index of the section (starting from 1)' do
expect(first_header.section_index).to eq 1
expect(second_header.section_index).to eq 2
end
end
end