diff --git a/.browserslistrc b/.browserslistrc index be9bfee68..6ade378c9 100644 --- a/.browserslistrc +++ b/.browserslistrc @@ -1,8 +1,10 @@ > 1% Chrome >= 50 -IE >= 11 Edge >= 14 Firefox >= 50 Opera >= 40 Safari >= 8 iOS >= 8 +# Although IE 11 shows a deprecation banner, we still support the transpilation option. +# IE 11 support may be removed starting from 01 / 06 / 2020. +IE >= 11 diff --git a/Gemfile.lock b/Gemfile.lock index b516e9f39..9a923b78b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -138,7 +138,7 @@ GEM selenium-webdriver case_transform (0.2) activesupport - chartkick (3.2.0) + chartkick (3.3.0) childprocess (0.9.0) ffi (~> 1.0, >= 1.0.11) chunky_png (1.3.11) diff --git a/README.md b/README.md index faf159341..94a643ecf 100644 --- a/README.md +++ b/README.md @@ -150,7 +150,7 @@ Pour les lister : `bin/rake -D support:`. ## Compatibilité navigateurs -L'application supporte les navigateurs récents : Firefox, Chrome, Safari, Edge et Internet Explorer 11 (voir `config/initializers/browser.rb`). +L'application gère les navigateurs récents, parmis lequels Firefox, Chrome, Safari et Edge (voir `config/initializers/browser.rb`). La compatibilité est testée par Browserstack.
[](https://www.browserstack.com/) diff --git a/app/assets/stylesheets/new_design/outdated_browser_banner.scss b/app/assets/stylesheets/new_design/outdated_browser_banner.scss index 3630c80b9..7b94f8a79 100644 --- a/app/assets/stylesheets/new_design/outdated_browser_banner.scss +++ b/app/assets/stylesheets/new_design/outdated_browser_banner.scss @@ -3,15 +3,23 @@ #outdated-browser-banner { width: 100%; - bottom: 0; - left: 0; margin: 0; padding: $default-padding; - color: #FFFFFF; - background-color: $medium-red; + color: $black; + background-color: $yellow; +} - a { - color: $lighter-blue; - text-decoration: underline; +.outdated-browser-icon { + margin-right: $default-padding; + font-size: 30px; + opacity: 0.7; +} + +.outdated-browser-buttons { + display: flex; + margin-left: auto; + + .button { + margin-left: $default-spacer; } } diff --git a/app/assets/stylesheets/new_design/user_signup.scss b/app/assets/stylesheets/new_design/user_signup.scss new file mode 100644 index 000000000..fe0b174db --- /dev/null +++ b/app/assets/stylesheets/new_design/user_signup.scss @@ -0,0 +1,24 @@ +@import "colors"; +@import "constants"; + +.suspect-email { + background-color: $orange-bg; + text-align: center; + margin-top: -$default-padding * 2; + margin-bottom: $default-padding * 2; + padding: ($default-padding - 2) $default-padding $default-padding $default-padding; + border-radius: 0 0 5px 5px; +} + +.email-suggestion-address { + font-weight: bold; +} + +.email-suggestion-title { + margin-bottom: $default-spacer; +} + +.email-suggestion-answer button { + margin: 0 $default-spacer / 2; + min-width: 66px; +} diff --git a/app/assets/stylesheets/outdated_browser_banner.scss b/app/assets/stylesheets/outdated_browser_banner.scss index 6ec1c0590..c4b2eb089 100644 --- a/app/assets/stylesheets/outdated_browser_banner.scss +++ b/app/assets/stylesheets/outdated_browser_banner.scss @@ -1,16 +1,25 @@ #outdated-browser-banner { - position: fixed; - text-align: center; - line-height: 2em; - color: #FFFFFF; - background-color: #990000; width: 100%; margin: 0; - bottom: 0; - left: 0; - z-index: 1000; - a { - color: #C3D9FF; + color: #333333; + background-color: #FEF3B8; + + .container { + display: flex; + width: auto; + align-items: center; + padding: 8px 16px; } } + +.outdated-browser-icon { + margin-right: 16px; + font-size: 30px; + opacity: 0.7; +} + +.outdated-browser-buttons { + display: flex; + margin-left: auto; +} diff --git a/app/controllers/new_administrateur/groupe_instructeurs_controller.rb b/app/controllers/new_administrateur/groupe_instructeurs_controller.rb index 620623ac5..f965781e5 100644 --- a/app/controllers/new_administrateur/groupe_instructeurs_controller.rb +++ b/app/controllers/new_administrateur/groupe_instructeurs_controller.rb @@ -61,7 +61,7 @@ module NewAdministrateur value: bad_emails.join(', ')) end - email_to_adds = correct_emails - groupe_instructeur.instructeurs.pluck(:email) + email_to_adds = correct_emails - groupe_instructeur.instructeurs.map(&:email) if email_to_adds.present? instructeurs = email_to_adds.map do |instructeur_email| @@ -159,8 +159,8 @@ module NewAdministrateur end def available_instructeur_emails - all = current_administrateur.instructeurs.pluck(:email) - assigned = groupe_instructeur.instructeurs.pluck(:email) + all = current_administrateur.instructeurs.map(&:email) + assigned = groupe_instructeur.instructeurs.map(&:email) (all - assigned).sort end end diff --git a/app/controllers/root_controller.rb b/app/controllers/root_controller.rb index 0014c7766..bcf28f57d 100644 --- a/app/controllers/root_controller.rb +++ b/app/controllers/root_controller.rb @@ -1,4 +1,6 @@ class RootController < ApplicationController + include ApplicationHelper + def index if administrateur_signed_in? return redirect_to admin_procedures_path @@ -69,4 +71,13 @@ class RootController < ApplicationController def suivi end + + def dismiss_outdated_browser + dismiss_outdated_browser_banner + + respond_to do |format| + format.html { redirect_back(fallback_location: root_path) } + format.js { render js: helpers.remove_element('#outdated-browser-banner') } + end + end end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 0fd4c8bb2..9929ab7f6 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -120,4 +120,15 @@ module ApplicationHelper def try_format_datetime(datetime) datetime.present? ? I18n.l(datetime) : '' end + + def dismiss_outdated_browser_banner + cookies[:dismissed_outdated_browser_banner] = { + value: 'true', + expires: 1.week.from_now + } + end + + def has_dismissed_outdated_browser_banner? + cookies[:dismissed_outdated_browser_banner] == 'true' + end end diff --git a/app/javascript/new_design/user-sign_up.js b/app/javascript/new_design/user-sign_up.js new file mode 100644 index 000000000..c2a8c4d70 --- /dev/null +++ b/app/javascript/new_design/user-sign_up.js @@ -0,0 +1,35 @@ +import { delegate, show, hide } from '@utils'; +import { suggest } from 'email-butler'; + +const userNewEmailSelector = '#new_user input#user_email'; +const passwordFieldSelector = '#new_user input#user_password'; +const suggestionsSelector = '.suspect-email'; +const emailSuggestionSelector = '.suspect-email .email-suggestion-address'; + +delegate('focusout', userNewEmailSelector, () => { + // When the user leaves the email input during account creation, we check if this account looks correct. + // If not (e.g if its "bidou@gmail.coo" or "john@yahoo.rf"), we attempt to suggest a fix for the invalid email. + const userEmailInput = document.querySelector(userNewEmailSelector); + const suggestedEmailSpan = document.querySelector(emailSuggestionSelector); + + const suggestion = suggest(userEmailInput.value); + if (suggestion && suggestion.full) { + suggestedEmailSpan.innerHTML = suggestion.full; + show(document.querySelector(suggestionsSelector)); + } else { + hide(document.querySelector(suggestionsSelector)); + } +}); + +export function acceptEmailSuggestion() { + const userEmailInput = document.querySelector(userNewEmailSelector); + const suggestedEmailSpan = document.querySelector(emailSuggestionSelector); + + userEmailInput.value = suggestedEmailSpan.innerHTML; + hide(document.querySelector(suggestionsSelector)); + document.querySelector(passwordFieldSelector).focus(); +} + +export function discardEmailSuggestionBox() { + hide(document.querySelector(suggestionsSelector)); +} diff --git a/app/javascript/packs/application.js b/app/javascript/packs/application.js index 8d0eb5d3c..a0e320c3a 100644 --- a/app/javascript/packs/application.js +++ b/app/javascript/packs/application.js @@ -40,6 +40,10 @@ import { } from '../new_design/state-button'; import { toggleChart } from '../new_design/toggle-chart'; import { replaceSemicolonByComma } from '../new_design/avis'; +import { + acceptEmailSuggestion, + discardEmailSuggestionBox +} from '../new_design/user-sign_up'; // This is the global application namespace where we expose helpers used from rails views const DS = { @@ -50,7 +54,9 @@ const DS = { motivationCancel, showImportJustificatif, toggleChart, - replaceSemicolonByComma + replaceSemicolonByComma, + acceptEmailSuggestion, + discardEmailSuggestionBox }; // Start Rails helpers @@ -59,19 +65,14 @@ Rails.start(); Turbolinks.start(); ActiveStorage.start(); -// If Turbolinks is imported via Webpacker (and thus not available globally), -// ReactRailsUJS will be unable to locate it. -// https://github.com/reactjs/react-rails#event-handling - -// eslint-disable-next-line no-undef -ReactRailsUJS.useContext(require.context('components', true)); -// Add Turbolinks to the global namespace: -window.Turbolinks = Turbolinks; -// Remove previous event handlers and add new ones: -ReactRailsUJS.detectEvents(); -// (Optional) Clean up global namespace: -delete window.Turbolinks; - // Expose globals window.DS = window.DS || DS; window.Chartkick = Chartkick; +// (Both Rails redirects and ReactRailsUJS expect Turbolinks to be globally available) +window.Turbolinks = Turbolinks; + +// Now that Turbolinks is globally exposed,configure ReactRailsUJS +// eslint-disable-next-line no-undef +ReactRailsUJS.useContext(require.context('components', true)); +// Remove previous event handlers and add new ones: +ReactRailsUJS.detectEvents(); diff --git a/app/views/instructeurs/procedures/show.html.haml b/app/views/instructeurs/procedures/show.html.haml index 229336345..0847097fd 100644 --- a/app/views/instructeurs/procedures/show.html.haml +++ b/app/views/instructeurs/procedures/show.html.haml @@ -64,6 +64,7 @@ %p.explication-onglet Les dossiers de cet onglet sont archivés : vous ne pouvez plus y répondre, et les demandeurs ne peuvent plus les modifier. - if @dossiers.present? || @current_filters.count > 0 + = paginate @dossiers %span.dropdown %button.button.dropdown-button Filtrer diff --git a/app/views/layouts/_main_container.html.haml b/app/views/layouts/_main_container.html.haml index a1d728b3a..ff74bc38d 100644 --- a/app/views/layouts/_main_container.html.haml +++ b/app/views/layouts/_main_container.html.haml @@ -1,4 +1,6 @@ #main-container{ class: "col-xs-#{main_container_size}" } + .row + = render partial: 'layouts/outdated_browser_banner' .row = render partial: 'layouts/flash_messages' .row diff --git a/app/views/layouts/_outdated_browser_banner.html.haml b/app/views/layouts/_outdated_browser_banner.html.haml index 3e36890a6..d60b38661 100644 --- a/app/views/layouts/_outdated_browser_banner.html.haml +++ b/app/views/layouts/_outdated_browser_banner.html.haml @@ -1,8 +1,15 @@ -/ See config/browser.rb -- if !browser.modern? +-# See config/browser.rb +- if !browser.modern? && !has_dismissed_outdated_browser_banner? #outdated-browser-banner - .container - Attention, votre navigateur (#{browser.name} #{browser.version}) est trop ancien pour utiliser demarches-simplifiees.fr : certaines parties du site ne fonctionneront pas correctement. Nous vous recommandons fortement de - %a{ href: "https://browser-update.org/fr/update.html", target: "_blank", rel: "noopener" }mettre à jour votre navigateur - %span<> - \. + .container.flex.align-center + .outdated-browser-icon + ⚠️ + .outdated-browser-test + %strong + #{browser.name} #{browser.version} est trop ancien pour utiliser demarches-simplifiees.fr. + %br + Certaines parties du site ne fonctionneront pas correctement. + .outdated-browser-buttons + = button_to 'Ignorer', dismiss_outdated_browser_path, method: :post, remote: true, class: 'button btn', title: 'Ne plus afficher cet avertissement pendant une semaine' + %a.btn.button.primary{ href: "https://browser-update.org/fr/update.html", target: "_blank", rel: "noopener" } + Mettre à jour mon navigateur diff --git a/app/views/layouts/application_old.html.haml b/app/views/layouts/application_old.html.haml index ce233e5f3..80fd1109e 100644 --- a/app/views/layouts/application_old.html.haml +++ b/app/views/layouts/application_old.html.haml @@ -19,7 +19,6 @@ = Gon::Base.render_data(camel_case: true, init: true) %body{ class: browser.platform.ios? ? 'ios' : nil } - = render partial: 'layouts/outdated_browser_banner' = render partial: 'layouts/pre_maintenance' - if staging? #beta diff --git a/app/views/root/accessibilite.html.haml b/app/views/root/accessibilite.html.haml index e790bf96a..5ed1165dd 100644 --- a/app/views/root/accessibilite.html.haml +++ b/app/views/root/accessibilite.html.haml @@ -14,7 +14,7 @@ %h2.new-h2 Défenseur des droits %p.new-p Si vous constatez un défaut d'accessibilité vous empêchant d'accéder à un contenu ou une fonctionnalité du site, que vous nous le signalez et que vous ne parvenez pas à obtenir une réponse rapide de notre part, vous êtes en droit de faire parvenir vos doléances ou une demande de saisine au Défenseur des droits. Plusieurs moyens sont à votre disposition : - %ul - %li un formulaire de contact ; - %li la liste du ou des délégués de votre région avec leurs informations de contact direct ; - %li une adresse postale : Le Défenseur des droits - 7 rue Saint-Florentin - 75409 Paris Cedex 08. + %ul + %li un formulaire de contact ; + %li la liste du ou des délégués de votre région avec leurs informations de contact direct ; + %li une adresse postale : Le Défenseur des droits - 7 rue Saint-Florentin - 75409 Paris Cedex 08. diff --git a/app/views/shared/dossiers/editable_champs/_textarea.html.haml b/app/views/shared/dossiers/editable_champs/_textarea.html.haml index 620f11c6d..a4666f22e 100644 --- a/app/views/shared/dossiers/editable_champs/_textarea.html.haml +++ b/app/views/shared/dossiers/editable_champs/_textarea.html.haml @@ -1,4 +1,4 @@ ~ form.text_area :value, - row: 6, + rows: 6, required: champ.mandatory?, value: html_to_string(champ.value) diff --git a/app/views/support/index.html.haml b/app/views/support/index.html.haml index 60374d8e6..20c89a6e3 100644 --- a/app/views/support/index.html.haml +++ b/app/views/support/index.html.haml @@ -72,10 +72,10 @@ .contact-champ = label_tag :text do Pièce jointe - .notice.hidden{ data: { 'contact-type-only': Helpscout::FormAdapter::TYPE_AMELIORATION } } - Une capture d’écran peut nous aider à identifier plus facilement l’endroit à améliorer. - .notice.hidden{ data: { 'contact-type-only': Helpscout::FormAdapter::TYPE_AUTRE } } - Une capture d’écran peut nous aider à identifier plus facilement le problème. + %p.notice.hidden{ data: { 'contact-type-only': Helpscout::FormAdapter::TYPE_AMELIORATION } } + Une capture d’écran peut nous aider à identifier plus facilement l’endroit à améliorer. + %p.notice.hidden{ data: { 'contact-type-only': Helpscout::FormAdapter::TYPE_AUTRE } } + Une capture d’écran peut nous aider à identifier plus facilement le problème. = file_field_tag :piece_jointe = hidden_field_tag :tags, @tags&.join(',') diff --git a/app/views/users/dossiers/merci.html.haml b/app/views/users/dossiers/merci.html.haml index d2da1ab4d..b3657867e 100644 --- a/app/views/users/dossiers/merci.html.haml +++ b/app/views/users/dossiers/merci.html.haml @@ -5,7 +5,7 @@ .merci .container - = image_tag('user/envoi-dossier.svg') + = image_tag('user/envoi-dossier.svg', alt: '') %h1 Merci ! %p.send Votre dossier sur la démarche diff --git a/app/views/users/registrations/new.html.haml b/app/views/users/registrations/new.html.haml index d21b184be..37bcc2387 100644 --- a/app/views/users/registrations/new.html.haml +++ b/app/views/users/registrations/new.html.haml @@ -8,6 +8,17 @@ = f.label :email, "Email" = f.text_field :email, autofocus: true, placeholder: "Votre adresse email" + .suspect-email + .email-suggestion-title + Voulez-vous dire + %span.email-suggestion-address blabla@gmail.com +  ? + .email-suggestion-answer + = button_tag type: 'button', class: 'button small', onclick: "DS.acceptEmailSuggestion()" do + Oui + = button_tag type: 'button', class: 'button small', onclick: "DS.discardEmailSuggestionBox()" do + Non + = f.label :password, "Mot de passe" = f.password_field :password, value: @user.password, placeholder: "8 caractères minimum" diff --git a/config/initializers/browser.rb b/config/initializers/browser.rb index 0e88068ad..f9d22d40b 100644 --- a/config/initializers/browser.rb +++ b/config/initializers/browser.rb @@ -1,8 +1,8 @@ # See .browserslistrc Browser.modern_rules.clear -Browser.modern_rules << -> b { b.chrome? && b.version.to_i >= 50 } -Browser.modern_rules << -> b { b.ie? && b.version.to_i >= 11 && !b.compatibility_view? } +Browser.modern_rules << -> b { b.chrome? && b.version.to_i >= 50 && !b.platform.ios? } Browser.modern_rules << -> b { b.edge? && b.version.to_i >= 14 && !b.compatibility_view? } -Browser.modern_rules << -> b { b.firefox? && b.version.to_i >= 50 } +Browser.modern_rules << -> b { b.firefox? && b.version.to_i >= 50 && !b.platform.ios? } Browser.modern_rules << -> b { b.opera? && b.version.to_i >= 40 } Browser.modern_rules << -> b { b.safari? && b.version.to_i >= 8 } +Browser.modern_rules << -> b { b.platform.ios? && b.platform.version.to_i >= 8 } diff --git a/config/routes.rb b/config/routes.rb index 47bca15b9..5d4908a9e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -125,6 +125,7 @@ Rails.application.routes.draw do get "patron" => "root#patron" get "accessibilite" => "root#accessibilite" get "suivi" => "root#suivi" + post "dismiss_outdated_browser" => "root#dismiss_outdated_browser" get "contact", to: "support#index" post "contact", to: "support#create" diff --git a/package.json b/package.json index 6a72eb949..8ccc7fce8 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "core-js": "^2.0.0", "debounce": "^1.2.0", "dom4": "^2.1.5", + "email-butler": "^1.0.12", "highcharts": "^6.1.2", "intersection-observer": "^0.7.0", "jquery": "^3.4.1", diff --git a/spec/features/outdated_browser_spec.rb b/spec/features/outdated_browser_spec.rb new file mode 100644 index 000000000..e6cb28efb --- /dev/null +++ b/spec/features/outdated_browser_spec.rb @@ -0,0 +1,31 @@ +require 'spec_helper' + +feature 'Outdated browsers support:' do + context 'when the user browser is outdated' do + before(:each) do + ie_11_user_agent = 'Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko' + Capybara.page.driver.header('user-agent', ie_11_user_agent) + end + + scenario 'a banner is displayed' do + visit new_user_session_path + expect(page).to have_content('Internet Explorer 11 est trop ancien') + end + + scenario 'the banner can be dismissed' do + visit new_user_session_path + expect(page).to have_content('Internet Explorer 11 est trop ancien') + + # The banner is hidden immediately + within '#outdated-browser-banner' do + click_on 'Ignorer' + end + expect(page).not_to have_content('Internet Explorer 11 est trop ancien') + expect(page).to have_current_path(new_user_session_path) + + # The banner is hidden after a refresh + page.refresh + expect(page).not_to have_content('Internet Explorer 11 est trop ancien') + end + end +end diff --git a/spec/features/users/sign_up_spec.rb b/spec/features/users/sign_up_spec.rb index 06eb1d831..ede736035 100644 --- a/spec/features/users/sign_up_spec.rb +++ b/spec/features/users/sign_up_spec.rb @@ -17,6 +17,39 @@ feature 'Signing up:' do expect(page).to have_current_path commencer_path(path: procedure.path) end + context 'when the user makes a typo in their email address' do + let(:procedure) { create :simple_procedure, :with_service } + + before do + visit commencer_path(path: procedure.path) + click_on 'Créer un compte demarches-simplifiees.fr' + expect(page).to have_selector('.suspect-email', visible: false) + fill_in 'Email', with: 'bidou@yahoo.rf' + fill_in 'Mot de passe', with: '12345' + end + + scenario 'they can accept the suggestion', js: true do + expect(page).to have_selector('.suspect-email', visible: true) + click_on 'Oui' + expect(page).to have_field("Email", :with => 'bidou@yahoo.fr') + expect(page).to have_selector('.suspect-email', visible: false) + end + + scenario 'they can discard the suggestion', js: true do + expect(page).to have_selector('.suspect-email', visible: true) + click_on 'Non' + expect(page).to have_field("Email", :with => 'bidou@yahoo.rf') + expect(page).to have_selector('.suspect-email', visible: false) + end + + scenario 'they can fix the typo themselves', js: true do + expect(page).to have_selector('.suspect-email', visible: true) + fill_in 'Email', with: 'bidou@yahoo.fr' + blur + expect(page).to have_selector('.suspect-email', visible: false) + end + end + scenario 'a new user can’t sign-up with too short password when visiting a procedure' do visit commencer_path(path: procedure.path) click_on 'Créer un compte demarches-simplifiees.fr' diff --git a/yarn.lock b/yarn.lock index 489582165..ff57a5c76 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3110,6 +3110,11 @@ elliptic@^6.0.0: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.0" +email-butler@^1.0.12: + version "1.0.12" + resolved "https://registry.yarnpkg.com/email-butler/-/email-butler-1.0.12.tgz#f504b45658fea6257dbc0f891be2cf822d29caa6" + integrity sha512-JcvRCjCpLp3fIHlP+KgdDadSmhETA/D4KUtIi3VnwEw2bJKilMOcd+xLJUHrD631bToxxxYRnxB3xTvAQeIRCQ== + emoji-regex@^7.0.1: version "7.0.3" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156"