Merge pull request #8300 from mfo/a11y/contact-apge
correctif(a11y.contact-page): #8058 (utiliser le type email sur l'input prenant l'email de l'usager), #8056 (ajuster les erreurs de contraste par l'usage des composants du DSFR)
This commit is contained in:
commit
fdb252ed7a
6 changed files with 84 additions and 144 deletions
|
@ -12,7 +12,6 @@
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: #FFFFFF;
|
color: #FFFFFF;
|
||||||
text-decoration: underline;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
48
app/javascript/controllers/support_controller.ts
Normal file
48
app/javascript/controllers/support_controller.ts
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
import { ApplicationController } from './application_controller';
|
||||||
|
import { hide, show } from '@utils';
|
||||||
|
|
||||||
|
export class SupportController extends ApplicationController {
|
||||||
|
static targets = ['inputRadio', 'content'];
|
||||||
|
|
||||||
|
declare readonly inputRadioTargets: HTMLInputElement[];
|
||||||
|
declare readonly contentTargets: HTMLElement[];
|
||||||
|
|
||||||
|
connect() {
|
||||||
|
this.inputRadioTargets.forEach((inputRadio) => {
|
||||||
|
inputRadio.addEventListener('change', this.onChange.bind(this));
|
||||||
|
inputRadio.addEventListener('keydown', this.onChange.bind(this));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onChange(event: Event) {
|
||||||
|
const target = event.target as HTMLInputElement;
|
||||||
|
const content = this.getContentForTarget(target);
|
||||||
|
|
||||||
|
this.contentTargets.forEach((content) => {
|
||||||
|
hide(content);
|
||||||
|
content.setAttribute('aria-hidden', 'true');
|
||||||
|
});
|
||||||
|
|
||||||
|
if (target.checked && content) {
|
||||||
|
show(content);
|
||||||
|
content.setAttribute('aria-hidden', 'false');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getLabelForTarget(target: HTMLInputElement) {
|
||||||
|
const labelSelector = `label[for="${target.id}"]`;
|
||||||
|
return document.querySelector(labelSelector);
|
||||||
|
}
|
||||||
|
|
||||||
|
getContentForTarget(target: HTMLInputElement) {
|
||||||
|
const label = this.getLabelForTarget(target);
|
||||||
|
if (!label) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const contentSelector = label.getAttribute('aria-controls');
|
||||||
|
|
||||||
|
if (contentSelector) {
|
||||||
|
return document.getElementById(contentSelector);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,7 +14,6 @@ import { registerControllers } from '../shared/stimulus-loader';
|
||||||
import '../new_design/form-validation';
|
import '../new_design/form-validation';
|
||||||
import '../new_design/procedure-context';
|
import '../new_design/procedure-context';
|
||||||
import '../new_design/procedure-form';
|
import '../new_design/procedure-form';
|
||||||
import '../new_design/support';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
toggleCondidentielExplanation,
|
toggleCondidentielExplanation,
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
/* Verify README of each component to insert them in the expected order. */
|
/* Verify README of each component to insert them in the expected order. */
|
||||||
@import '@gouvfr/dsfr/dist/component/alert/alert.css';
|
@import '@gouvfr/dsfr/dist/component/alert/alert.css';
|
||||||
|
@import '@gouvfr/dsfr/dist/component/radio/radio.css';
|
||||||
@import '@gouvfr/dsfr/dist/component/badge/badge.css';
|
@import '@gouvfr/dsfr/dist/component/badge/badge.css';
|
||||||
@import '@gouvfr/dsfr/dist/component/breadcrumb/breadcrumb.css';
|
@import '@gouvfr/dsfr/dist/component/breadcrumb/breadcrumb.css';
|
||||||
@import '@gouvfr/dsfr/dist/component/callout/callout.css';
|
@import '@gouvfr/dsfr/dist/component/callout/callout.css';
|
||||||
|
|
|
@ -1,110 +0,0 @@
|
||||||
//
|
|
||||||
// This content is inspired by w3c aria example, rewritten for better RGAA compatibility.
|
|
||||||
// https://www.w3.org/TR/wai-aria-practices-1.1/examples/disclosure/disclosure-faq.html
|
|
||||||
//
|
|
||||||
|
|
||||||
class ButtonExpand {
|
|
||||||
constructor(domNode) {
|
|
||||||
this.domNode = domNode;
|
|
||||||
|
|
||||||
this.keyCode = Object.freeze({
|
|
||||||
RETURN: 13
|
|
||||||
});
|
|
||||||
|
|
||||||
this.allButtons = [];
|
|
||||||
this.controlledNode = false;
|
|
||||||
|
|
||||||
var id = this.domNode.getAttribute('aria-controls');
|
|
||||||
|
|
||||||
if (id) {
|
|
||||||
this.controlledNode = document.getElementById(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.radioInput = this.domNode.querySelector('input[type="radio"]');
|
|
||||||
|
|
||||||
this.hideContent();
|
|
||||||
|
|
||||||
this.domNode.addEventListener('keydown', this.handleKeydown.bind(this));
|
|
||||||
this.domNode.addEventListener('click', this.handleClick.bind(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
showContent() {
|
|
||||||
this.radioInput.checked = true;
|
|
||||||
|
|
||||||
if (this.controlledNode) {
|
|
||||||
this.controlledNode.setAttribute('aria-hidden', 'false');
|
|
||||||
this.controlledNode.classList.remove('hidden');
|
|
||||||
}
|
|
||||||
|
|
||||||
this.allButtons.forEach((b) => {
|
|
||||||
if (b != this) {
|
|
||||||
b.hideContent();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
hideContent() {
|
|
||||||
this.radioInput.checked = false;
|
|
||||||
|
|
||||||
if (this.controlledNode) {
|
|
||||||
this.controlledNode.setAttribute('aria-hidden', 'true');
|
|
||||||
this.controlledNode.classList.add('hidden');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleExpand() {
|
|
||||||
if (
|
|
||||||
this.controlledNode &&
|
|
||||||
this.controlledNode.getAttribute('aria-hidden') === 'true'
|
|
||||||
) {
|
|
||||||
this.showContent();
|
|
||||||
} else {
|
|
||||||
this.hideContent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setAllButtons(buttons) {
|
|
||||||
this.allButtons = buttons;
|
|
||||||
}
|
|
||||||
|
|
||||||
handleKeydown(event) {
|
|
||||||
switch (event.keyCode) {
|
|
||||||
case this.keyCode.RETURN:
|
|
||||||
this.showContent();
|
|
||||||
|
|
||||||
event.stopPropagation();
|
|
||||||
event.preventDefault();
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handleClick() {
|
|
||||||
// NOTE: click event is also fired on input and label activations
|
|
||||||
// ie., not necessarily by a mouse click but any user inputs, like keyboard navigation with arrows keys.
|
|
||||||
// Cf https://www.w3.org/TR/2012/WD-html5-20121025/content-models.html#interactive-content
|
|
||||||
|
|
||||||
this.showContent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Initialize Hide/Show Buttons */
|
|
||||||
|
|
||||||
if (document.querySelector('#contact-form')) {
|
|
||||||
window.addEventListener(
|
|
||||||
'DOMContentLoaded',
|
|
||||||
function () {
|
|
||||||
var buttons = document.querySelectorAll('fieldset[name=type] label');
|
|
||||||
var expandButtons = [];
|
|
||||||
|
|
||||||
buttons.forEach((button) => {
|
|
||||||
var be = new ButtonExpand(button);
|
|
||||||
expandButtons.push(be);
|
|
||||||
});
|
|
||||||
expandButtons.forEach((button) => button.setAllButtons(expandButtons));
|
|
||||||
},
|
|
||||||
false
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -7,7 +7,7 @@
|
||||||
%h1.new-h1
|
%h1.new-h1
|
||||||
= t('.contact')
|
= t('.contact')
|
||||||
|
|
||||||
= form_tag contact_path, method: :post, multipart: true, class: 'form' do
|
= form_tag contact_path, method: :post, multipart: true, class: 'fr-form-group', data: {controller: :support } do
|
||||||
|
|
||||||
.description
|
.description
|
||||||
%h2= t('.intro_html')
|
%h2= t('.intro_html')
|
||||||
|
@ -15,59 +15,62 @@
|
||||||
%p.mandatory-explanation= t('asterisk_html', scope: [:utils])
|
%p.mandatory-explanation= t('asterisk_html', scope: [:utils])
|
||||||
|
|
||||||
- if !user_signed_in?
|
- if !user_signed_in?
|
||||||
.contact-champ
|
.fr-input-group
|
||||||
= label_tag :email do
|
= label_tag :email, class: 'fr-label' do
|
||||||
Email
|
Email
|
||||||
%span.mandatory *
|
%span.mandatory *
|
||||||
= text_field_tag :email, params[:email], required: true, autocomplete: 'email'
|
= email_field_tag :email, params[:email], required: true, autocomplete: 'email', class: 'fr-input'
|
||||||
|
|
||||||
%fieldset.radios.vertical{ name: "type" }
|
%fieldset.fr-fieldset{ name: "type" }
|
||||||
%legend.form-label
|
%legend.fr-fieldset__legend
|
||||||
= t('.your_question')
|
= t('.your_question')
|
||||||
%span.mandatory *
|
%span.mandatory *
|
||||||
|
.fr-fieldset__content
|
||||||
|
- @options.each do |(question, question_type, link)|
|
||||||
|
.fr-radio-group
|
||||||
|
= radio_button_tag :type, question_type, false, required: true, data: {"support-target": "inputRadio" }
|
||||||
|
= label_tag "type_#{question_type}", { 'aria-controls': link ? "card-#{question_type}" : nil, class: 'fr-label' } do
|
||||||
|
= question
|
||||||
|
|
||||||
- @options.each do |(question, question_type, link)|
|
- if link.present?
|
||||||
= label_tag "type_#{question_type}", { 'aria-controls': link ? "card-#{question_type}" : nil } do
|
.support.card.featured.mb-4.ml-4.hidden{ id: "card-#{question_type}", "aria-hidden": true , data: { "support-target": "content" } }
|
||||||
= radio_button_tag :type, question_type, false, required: true
|
.card-title
|
||||||
= question
|
= t('.our_answer')
|
||||||
|
.card-content
|
||||||
- if link.present?
|
-# i18n-tasks-use t("support.index.#{question_type}.answer_html")
|
||||||
.support.card.featured.mb-4.ml-5.hidden{ id: "card-#{question_type}", "aria-hidden": true }
|
= t('answer_html', scope: [:support, :index, question_type], base_url: APPLICATION_BASE_URL, "link_#{question_type}": link)
|
||||||
.card-title
|
|
||||||
= t('.our_answer')
|
|
||||||
.card-content
|
|
||||||
-# i18n-tasks-use t("support.index.#{question_type}.answer_html")
|
|
||||||
= t('answer_html', scope: [:support, :index, question_type], base_url: APPLICATION_BASE_URL, "link_#{question_type}": link)
|
|
||||||
|
|
||||||
|
|
||||||
.contact-champ
|
.fr-input-group
|
||||||
= label_tag :dossier_id, t('file_number', scope: [:utils])
|
= label_tag :dossier_id, t('file_number', scope: [:utils]), class: 'fr-label'
|
||||||
= text_field_tag :dossier_id, @dossier_id
|
= text_field_tag :dossier_id, @dossier_id, class: 'fr-input'
|
||||||
|
|
||||||
.contact-champ
|
.fr-input-group
|
||||||
= label_tag :subject do
|
= label_tag :subject, class: 'fr-label' do
|
||||||
= t('subject', scope: [:utils])
|
= t('subject', scope: [:utils])
|
||||||
%span.mandatory *
|
%span.mandatory *
|
||||||
= text_field_tag :subject, params[:subject], required: true
|
= text_field_tag :subject, params[:subject], required: true, class: 'fr-input'
|
||||||
|
|
||||||
.contact-champ
|
.fr-input-group
|
||||||
= label_tag :text do
|
= label_tag :text, class: 'fr-label' do
|
||||||
= t('message', scope: [:utils])
|
= t('message', scope: [:utils])
|
||||||
%span.mandatory *
|
%span.mandatory *
|
||||||
= text_area_tag :text, params[:text], rows: 6, required: true
|
= text_area_tag :text, params[:text], rows: 6, required: true, class: 'fr-input'
|
||||||
|
|
||||||
.contact-champ
|
.fr-upload-group
|
||||||
= label_tag :piece_jointe do
|
= label_tag :piece_jointe, class: 'fr-label' do
|
||||||
= t('pj', scope: [:utils])
|
= t('pj', scope: [:utils])
|
||||||
|
%span.fr-hint-text Taille maximale : 200 Mo. Formats supportés : jpg, png, pdf.
|
||||||
|
|
||||||
%p.notice.hidden{ data: { 'contact-type-only': Helpscout::FormAdapter::TYPE_AMELIORATION } }
|
%p.notice.hidden{ data: { 'contact-type-only': Helpscout::FormAdapter::TYPE_AMELIORATION } }
|
||||||
= t('.notice_pj_product')
|
= t('.notice_pj_product')
|
||||||
%p.notice.hidden{ data: { 'contact-type-only': Helpscout::FormAdapter::TYPE_AUTRE } }
|
%p.notice.hidden{ data: { 'contact-type-only': Helpscout::FormAdapter::TYPE_AUTRE } }
|
||||||
= t('.notice_pj_other')
|
= t('.notice_pj_other')
|
||||||
= file_field_tag :piece_jointe
|
= file_field_tag :piece_jointe, class: 'fr-upload', max: 200.megabytes
|
||||||
|
|
||||||
= hidden_field_tag :tags, @tags&.join(',')
|
= hidden_field_tag :tags, @tags&.join(',')
|
||||||
|
|
||||||
= invisible_captcha
|
= invisible_captcha
|
||||||
|
|
||||||
.send-wrapper
|
.send-wrapper.fr-my-3w
|
||||||
= button_tag t('send_mail', scope: [:utils]), type: :submit, class: 'button send primary'
|
= button_tag t('send_mail', scope: [:utils]), type: :submit, class: 'fr-btn send'
|
||||||
|
|
Loading…
Reference in a new issue