Merge pull request #7572 from tchak/refactor-use-turbo-instead-of-ujs
refactor(ujs): remove old ujs helpers
This commit is contained in:
commit
9cc5415a1a
18 changed files with 64 additions and 272 deletions
|
@ -53,7 +53,7 @@ class FranceConnect::ParticulierController < ApplicationController
|
|||
if !user.can_france_connect?
|
||||
flash.alert = t('errors.messages.france_connect.forbidden_html', reset_link: new_user_password_path)
|
||||
|
||||
render js: ajax_redirect(root_path)
|
||||
redirect_to root_path
|
||||
else
|
||||
@fci.update(user: user)
|
||||
@fci.delete_merge_token!
|
||||
|
@ -63,8 +63,6 @@ class FranceConnect::ParticulierController < ApplicationController
|
|||
end
|
||||
else
|
||||
flash.alert = t('france_connect.particulier.flash.invalid_password')
|
||||
|
||||
render js: helpers.render_flash
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -112,10 +110,7 @@ class FranceConnect::ParticulierController < ApplicationController
|
|||
if @fci.nil? || !@fci.valid_for_merge?
|
||||
flash.alert = t('france_connect.particulier.flash.merger_token_expired', application_name: APPLICATION_NAME)
|
||||
|
||||
respond_to do |format|
|
||||
format.html { redirect_to root_path }
|
||||
format.js { render js: ajax_redirect(root_path) }
|
||||
end
|
||||
redirect_to root_path
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -134,12 +129,7 @@ class FranceConnect::ParticulierController < ApplicationController
|
|||
|
||||
user.update_attribute('loged_in_with_france_connect', User.loged_in_with_france_connects.fetch(:particulier))
|
||||
|
||||
redirection_location = stored_location_for(current_user) || root_path(current_user)
|
||||
|
||||
respond_to do |format|
|
||||
format.html { redirect_to redirection_location }
|
||||
format.js { render js: ajax_redirect(root_path) }
|
||||
end
|
||||
redirect_to stored_location_for(current_user) || root_path(current_user)
|
||||
end
|
||||
|
||||
def redirect_france_connect_error_connection
|
||||
|
|
|
@ -40,77 +40,6 @@ module ApplicationHelper
|
|||
tag.div(**html.merge(data: { controller: 'react', react_component_value: name, react_props_value: props.to_json }))
|
||||
end
|
||||
|
||||
def render_to_element(selector, partial:, outer: false, locals: {})
|
||||
method = outer ? 'outerHTML' : 'innerHTML'
|
||||
html = escape_javascript(render partial: partial, locals: locals)
|
||||
# rubocop:disable Rails/OutputSafety
|
||||
raw("document.querySelector('#{selector}').#{method} = \"#{html}\";")
|
||||
# rubocop:enable Rails/OutputSafety
|
||||
end
|
||||
|
||||
def append_to_element(selector, partial:, locals: {})
|
||||
html = escape_javascript(render partial: partial, locals: locals)
|
||||
# rubocop:disable Rails/OutputSafety
|
||||
raw("document.querySelector('#{selector}').insertAdjacentHTML('beforeend', \"#{html}\");")
|
||||
# rubocop:enable Rails/OutputSafety
|
||||
end
|
||||
|
||||
def render_flash(timeout: false, sticky: false, fixed: false)
|
||||
if flash.any?
|
||||
html = render_to_element('#flash_messages', partial: 'layouts/flash_messages', locals: { sticky: sticky, fixed: fixed }, outer: true)
|
||||
flash.clear
|
||||
if timeout
|
||||
html += remove_element('#flash_messages', timeout: timeout, inner: true)
|
||||
end
|
||||
html
|
||||
end
|
||||
end
|
||||
|
||||
def remove_element(selector, timeout: 0, inner: false)
|
||||
script = "(function() {";
|
||||
script << "var el = document.querySelector('#{selector}');"
|
||||
method = (inner ? "el.innerHTML = ''" : "el.parentNode.removeChild(el)")
|
||||
if timeout.present? && timeout > 0
|
||||
script << "if (el) { setTimeout(function() { #{method}; }, #{timeout}); }"
|
||||
else
|
||||
script << "if (el) { #{method} };"
|
||||
end
|
||||
script << "})();"
|
||||
# rubocop:disable Rails/OutputSafety
|
||||
raw(script);
|
||||
# rubocop:enable Rails/OutputSafety
|
||||
end
|
||||
|
||||
def show_element(selector)
|
||||
# rubocop:disable Rails/OutputSafety
|
||||
raw("document.querySelector('#{selector}').classList.remove('hidden');")
|
||||
# rubocop:enable Rails/OutputSafety
|
||||
end
|
||||
|
||||
def focus_element(selector)
|
||||
# rubocop:disable Rails/OutputSafety
|
||||
raw("document.querySelector('#{selector}').focus();")
|
||||
# rubocop:enable Rails/OutputSafety
|
||||
end
|
||||
|
||||
def disable_element(selector)
|
||||
# rubocop:disable Rails/OutputSafety
|
||||
raw("document.querySelector('#{selector}').disabled = true;")
|
||||
# rubocop:enable Rails/OutputSafety
|
||||
end
|
||||
|
||||
def enable_element(selector)
|
||||
# rubocop:disable Rails/OutputSafety
|
||||
raw("document.querySelector('#{selector}').disabled = false;")
|
||||
# rubocop:enable Rails/OutputSafety
|
||||
end
|
||||
|
||||
def fire_event(event_name, data)
|
||||
# rubocop:disable Rails/OutputSafety
|
||||
raw("DS.fire('#{event_name}', #{raw(data)});")
|
||||
# rubocop:enable Rails/OutputSafety
|
||||
end
|
||||
|
||||
def current_email
|
||||
current_user&.email ||
|
||||
current_instructeur&.email ||
|
||||
|
|
|
@ -5,17 +5,14 @@ import { Application } from '@hotwired/stimulus';
|
|||
import '@gouvfr/dsfr/dist/dsfr.module.js';
|
||||
|
||||
import '../shared/activestorage/ujs';
|
||||
import '../shared/remote-poller';
|
||||
import '../shared/safari-11-file-xhr-workaround';
|
||||
import '../shared/safari-11-empty-file-workaround';
|
||||
import '../shared/toggle-target';
|
||||
import '../shared/ujs-error-handling';
|
||||
|
||||
import { registerControllers } from '../shared/stimulus-loader';
|
||||
|
||||
import '../new_design/form-validation';
|
||||
import '../new_design/procedure-context';
|
||||
import '../new_design/procedure-form';
|
||||
import '../new_design/spinner';
|
||||
import '../new_design/support';
|
||||
|
||||
import {
|
||||
|
@ -31,11 +28,7 @@ import {
|
|||
acceptEmailSuggestion,
|
||||
discardEmailSuggestionBox
|
||||
} from '../new_design/user-sign_up';
|
||||
import {
|
||||
showFusion,
|
||||
showNewAccount,
|
||||
showNewAccountPasswordConfirmation
|
||||
} from '../new_design/fc-fusion';
|
||||
import { showFusion, showNewAccount } from '../new_design/fc-fusion';
|
||||
|
||||
const application = Application.start();
|
||||
registerControllers(application);
|
||||
|
@ -49,7 +42,6 @@ const DS = {
|
|||
showImportJustificatif,
|
||||
showFusion,
|
||||
showNewAccount,
|
||||
showNewAccountPasswordConfirmation,
|
||||
replaceSemicolonByComma,
|
||||
acceptEmailSuggestion,
|
||||
discardEmailSuggestionBox
|
||||
|
|
|
@ -3,17 +3,11 @@ import { show, hide } from '@utils';
|
|||
export function showFusion() {
|
||||
show(document.querySelector('.fusion'));
|
||||
hide(document.querySelector('.new-account'));
|
||||
hide(document.querySelector('.new-account-password-confirmation'));
|
||||
hide(document.querySelector('#new-account-password-confirmation'));
|
||||
}
|
||||
|
||||
export function showNewAccount() {
|
||||
hide(document.querySelector('.fusion'));
|
||||
show(document.querySelector('.new-account'));
|
||||
hide(document.querySelector('.new-account-password-confirmation'));
|
||||
}
|
||||
|
||||
export function showNewAccountPasswordConfirmation() {
|
||||
hide(document.querySelector('.fusion'));
|
||||
hide(document.querySelector('.new-account'));
|
||||
show(document.querySelector('.new-account-password-confirmation'));
|
||||
hide(document.querySelector('#new-account-password-confirmation'));
|
||||
}
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
import { show, hide, delegate } from '@utils';
|
||||
|
||||
function showSpinner() {
|
||||
[...document.querySelectorAll('.spinner')].forEach(show);
|
||||
}
|
||||
|
||||
function hideSpinner() {
|
||||
[...document.querySelectorAll('.spinner')].forEach(hide);
|
||||
}
|
||||
|
||||
delegate('ajax:complete', '[data-spinner]', hideSpinner);
|
||||
delegate('ajax:stopped', '[data-spinner]', hideSpinner);
|
||||
delegate('ajax:send', '[data-spinner]', showSpinner);
|
|
@ -1,101 +0,0 @@
|
|||
import { httpRequest, delegate } from '@utils';
|
||||
|
||||
addEventListener('DOMContentLoaded', () => {
|
||||
attachementPoller.deactivate();
|
||||
exportPoller.deactivate();
|
||||
|
||||
const attachments = document.querySelectorAll('[data-attachment-poll-url]');
|
||||
const exports = document.querySelectorAll('[data-export-poll-url]');
|
||||
|
||||
for (let { dataset } of attachments) {
|
||||
attachementPoller.add(dataset.attachmentPollUrl);
|
||||
}
|
||||
|
||||
for (let { dataset } of exports) {
|
||||
exportPoller.add(dataset.exportPollUrl);
|
||||
}
|
||||
});
|
||||
|
||||
addEventListener('attachment:update', ({ detail: { url } }) => {
|
||||
attachementPoller.add(url);
|
||||
});
|
||||
|
||||
addEventListener('export:update', ({ detail: { url } }) => {
|
||||
exportPoller.add(url);
|
||||
});
|
||||
|
||||
delegate('click', '[data-attachment-refresh]', (event) => {
|
||||
event.preventDefault();
|
||||
attachementPoller.check();
|
||||
});
|
||||
|
||||
// Periodically check the state of a set of URLs.
|
||||
//
|
||||
// Each time the given URL is requested, the matching `show.js.erb` view is rendered,
|
||||
// causing the state to be refreshed.
|
||||
//
|
||||
// This is used mainly to refresh attachments during the anti-virus check,
|
||||
// but also to refresh the state of a pending spreadsheet export.
|
||||
class RemotePoller {
|
||||
urls = new Set();
|
||||
timeout;
|
||||
checks = 0;
|
||||
|
||||
constructor(settings = {}) {
|
||||
this.interval = settings.interval;
|
||||
this.maxChecks = settings.maxChecks;
|
||||
}
|
||||
|
||||
get isEnabled() {
|
||||
return this.checks <= this.maxChecks;
|
||||
}
|
||||
|
||||
get isActive() {
|
||||
return this.timeout !== undefined;
|
||||
}
|
||||
|
||||
add(url) {
|
||||
if (this.isEnabled) {
|
||||
if (!this.isActive) {
|
||||
this.activate();
|
||||
}
|
||||
this.urls.add(url);
|
||||
}
|
||||
}
|
||||
|
||||
check() {
|
||||
let urls = this.urls;
|
||||
this.reset();
|
||||
for (let url of urls) {
|
||||
// Start the request. The JS payload in the response will update the page.
|
||||
// (Errors are ignored, because background tasks shouldn't report errors to the user.)
|
||||
httpRequest(url)
|
||||
.js()
|
||||
.catch(() => {});
|
||||
}
|
||||
}
|
||||
|
||||
activate() {
|
||||
clearTimeout(this.timeout);
|
||||
this.timeout = setTimeout(() => {
|
||||
this.checks++;
|
||||
this.currentInterval = this.interval * 1.5;
|
||||
this.check();
|
||||
}, this.currentInterval);
|
||||
}
|
||||
|
||||
deactivate() {
|
||||
this.checks = 0;
|
||||
this.currentInterval = this.interval;
|
||||
this.reset();
|
||||
}
|
||||
|
||||
reset() {
|
||||
clearTimeout(this.timeout);
|
||||
this.urls = new Set();
|
||||
this.timeout = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
const attachementPoller = new RemotePoller({ interval: 3000, maxChecks: 5 });
|
||||
const exportPoller = new RemotePoller({ interval: 6000, maxChecks: 10 });
|
36
app/javascript/shared/safari-11-empty-file-workaround.ts
Normal file
36
app/javascript/shared/safari-11-empty-file-workaround.ts
Normal file
|
@ -0,0 +1,36 @@
|
|||
// iOS 11.3 Safari / macOS Safari 11.1 empty <input type="file"> XHR bug workaround.
|
||||
// This should work with every modern browser which supports ES5 (including IE9).
|
||||
// https://stackoverflow.com/questions/49614091/safari-11-1-ajax-xhr-form-submission-fails-when-inputtype-file-is-empty
|
||||
// https://github.com/rails/rails/issues/32440
|
||||
|
||||
document.documentElement.addEventListener(
|
||||
'turbo:before-fetch-request',
|
||||
(event) => {
|
||||
const target = event.target as Element;
|
||||
const inputs = target.querySelectorAll<HTMLInputElement>(
|
||||
'input[type="file"]:not([disabled])'
|
||||
);
|
||||
for (const input of inputs) {
|
||||
if (input.files?.length == 0) {
|
||||
input.setAttribute('data-safari-temp-disabled', 'true');
|
||||
input.setAttribute('disabled', '');
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
document.documentElement.addEventListener(
|
||||
'turbo:before-fetch-response',
|
||||
(event) => {
|
||||
const target = event.target as Element;
|
||||
const inputs = target.querySelectorAll(
|
||||
'input[type="file"][data-safari-temp-disabled]'
|
||||
);
|
||||
for (const input of inputs) {
|
||||
input.removeAttribute('data-safari-temp-disabled');
|
||||
input.removeAttribute('disabled');
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export {};
|
|
@ -1,26 +0,0 @@
|
|||
// iOS 11.3 Safari / macOS Safari 11.1 empty <input type="file"> XHR bug workaround.
|
||||
// This should work with every modern browser which supports ES5 (including IE9).
|
||||
// https://stackoverflow.com/questions/49614091/safari-11-1-ajax-xhr-form-submission-fails-when-inputtype-file-is-empty
|
||||
// https://github.com/rails/rails/issues/32440
|
||||
|
||||
document.addEventListener('ajax:before', function (e) {
|
||||
let inputs = e.target.querySelectorAll('input[type="file"]:not([disabled])');
|
||||
inputs.forEach(function (input) {
|
||||
if (input.files.length > 0) {
|
||||
return;
|
||||
}
|
||||
input.setAttribute('data-safari-temp-disabled', 'true');
|
||||
input.setAttribute('disabled', '');
|
||||
});
|
||||
});
|
||||
|
||||
// You should call this by yourself when you aborted an ajax request by stopping a event in ajax:before hook.
|
||||
document.addEventListener('ajax:beforeSend', function (e) {
|
||||
let inputs = e.target.querySelectorAll(
|
||||
'input[type="file"][data-safari-temp-disabled]'
|
||||
);
|
||||
inputs.forEach(function (input) {
|
||||
input.removeAttribute('data-safari-temp-disabled');
|
||||
input.removeAttribute('disabled');
|
||||
});
|
||||
});
|
|
@ -1,8 +0,0 @@
|
|||
// For links and requests done through rails-ujs (mostly data-remote links),
|
||||
// redirect to the sign-in page when the server responds '401 Unauthorized'.
|
||||
document.addEventListener('ajax:error', (event) => {
|
||||
const [, , xhr] = event.detail;
|
||||
if (xhr && xhr.status == 401) {
|
||||
location.reload(); // reload whole page so Devise will redirect to sign-in
|
||||
}
|
||||
});
|
|
@ -16,7 +16,7 @@
|
|||
url: allow_expert_review_admin_procedure_path(@procedure),
|
||||
html: { class: 'form procedure-form__column--form no-background' } do |f|
|
||||
%label.toggle-switch
|
||||
= f.check_box :allow_expert_review, class: 'toggle-switch-checkbox', onchange: 'this.form.submit()'
|
||||
= f.check_box :allow_expert_review, class: 'toggle-switch-checkbox', onchange: 'this.form.requestSubmit()'
|
||||
%span.toggle-switch-control.round
|
||||
%span.toggle-switch-label.on
|
||||
%span.toggle-switch-label.off
|
||||
|
@ -30,7 +30,7 @@
|
|||
url: experts_require_administrateur_invitation_admin_procedure_path(@procedure),
|
||||
html: { class: 'form procedure-form__column--form no-background' } do |f|
|
||||
%label.toggle-switch
|
||||
= f.check_box :experts_require_administrateur_invitation, class: 'toggle-switch-checkbox', onchange: 'this.form.submit()'
|
||||
= f.check_box :experts_require_administrateur_invitation, class: 'toggle-switch-checkbox', onchange: 'this.form.requestSubmit()'
|
||||
%span.toggle-switch-control.round
|
||||
%span.toggle-switch-label.on
|
||||
%span.toggle-switch-label.off
|
||||
|
@ -76,12 +76,11 @@
|
|||
%td.text-center
|
||||
= form_for expert_procedure,
|
||||
url: admin_procedure_expert_path(id: expert_procedure),
|
||||
remote: true,
|
||||
method: :put,
|
||||
authenticity_token: true,
|
||||
data: { turbo: true },
|
||||
html: { class: 'form procedure-form__column--form no-background' } do |f|
|
||||
%label.toggle-switch
|
||||
= f.check_box :allow_decision_access, class: 'toggle-switch-checkbox', onchange: 'this.form.submit()'
|
||||
= f.check_box :allow_decision_access, class: 'toggle-switch-checkbox', onchange: 'this.form.requestSubmit()'
|
||||
%span.toggle-switch-control.round
|
||||
%span.toggle-switch-label.on
|
||||
%span.toggle-switch-label.off
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
%br
|
||||
= t('.fill_in_password')
|
||||
|
||||
= form_tag france_connect_particulier_merge_with_existing_account_path, remote: true, class: 'mt-2 form fconnect-form' do
|
||||
= form_tag france_connect_particulier_merge_with_existing_account_path, data: { turbo: true }, class: 'mt-2 form fconnect-form' do
|
||||
= hidden_field_tag :merge_token, merge_token
|
||||
= hidden_field_tag :email, email
|
||||
= label_tag :password, t('views.registrations.new.password_label', min_length: 8)
|
||||
|
|
|
@ -19,9 +19,9 @@
|
|||
.fusion.hidden
|
||||
%p= t('.title_fill_in_password')
|
||||
|
||||
= form_tag france_connect_particulier_merge_with_existing_account_path, remote: true, class: 'mt-2 form fconnect-form' do
|
||||
= hidden_field_tag :merge_token, @fci.merge_token
|
||||
= hidden_field_tag :email, @fci.email_france_connect
|
||||
= form_tag france_connect_particulier_merge_with_existing_account_path, data: { turbo: true }, class: 'mt-2 form fconnect-form' do
|
||||
= hidden_field_tag :merge_token, @fci.merge_token, id: dom_id(@fci, :fusion_merge_token)
|
||||
= hidden_field_tag :email, @fci.email_france_connect, id: dom_id(@fci, :fusion_email)
|
||||
|
||||
= label_tag :password, t('views.registrations.new.password_label', min_length: 8)
|
||||
= password_field_tag :password, nil, autocomplete: 'current-password', class: 'mb-1'
|
||||
|
@ -36,11 +36,11 @@
|
|||
.new-account.hidden
|
||||
%p= t('.title_fill_in_email', application_name: APPLICATION_NAME)
|
||||
|
||||
= form_tag france_connect_particulier_merge_with_new_account_path, remote: true, class: 'mt-2 form' do
|
||||
= hidden_field_tag :merge_token, @fci.merge_token
|
||||
= label_tag :email, t('views.registrations.new.email_label')
|
||||
= email_field_tag :email, "", required: true
|
||||
= form_tag france_connect_particulier_merge_with_new_account_path, data: { turbo: true }, class: 'mt-2 form' do
|
||||
= hidden_field_tag :merge_token, @fci.merge_token, id: dom_id(@fci, :new_account_merge_token)
|
||||
= label_tag :email, t('views.registrations.new.email_label'), for: dom_id(@fci, :new_account_email)
|
||||
= email_field_tag :email, "", required: true, id: dom_id(@fci, :new_account_email)
|
||||
= submit_tag t('.button_use_this_email'), class: 'button primary'
|
||||
|
||||
|
||||
.new-account-password-confirmation.hidden
|
||||
#new-account-password-confirmation.hidden
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
<%= render_to_element('.new-account-password-confirmation', partial: 'password_confirmation', locals: { email: @email, merge_token: @merge_token }) %>
|
||||
|
||||
DS.showNewAccountPasswordConfirmation();
|
|
@ -0,0 +1,4 @@
|
|||
= turbo_stream.update 'new-account-password-confirmation', partial: 'password_confirmation', locals: { email: @email, merge_token: @merge_token }
|
||||
= turbo_stream.hide_all '.fusion'
|
||||
= turbo_stream.hide_all '.new-account'
|
||||
= turbo_stream.show 'new-account-password-confirmation'
|
|
@ -1 +0,0 @@
|
|||
= render_flash(sticky: true)
|
|
@ -198,7 +198,7 @@ describe FranceConnect::ParticulierController, type: :controller do
|
|||
let(:merge_token) { fci.create_merge_token! }
|
||||
let(:email) { 'EXISTING_account@a.com ' }
|
||||
let(:password) { 'my-s3cure-p4ssword' }
|
||||
let(:format) { :js }
|
||||
let(:format) { :turbo_stream }
|
||||
|
||||
subject { post :merge_with_existing_account, params: { merge_token: merge_token, email: email, password: password }, format: format }
|
||||
|
||||
|
@ -309,7 +309,7 @@ describe FranceConnect::ParticulierController, type: :controller do
|
|||
let(:fci) { FranceConnectInformation.create!(user_info) }
|
||||
let(:merge_token) { fci.create_merge_token! }
|
||||
let(:email) { ' Account@a.com ' }
|
||||
let(:format) { :js }
|
||||
let(:format) { :turbo_stream }
|
||||
|
||||
subject { post :merge_with_new_account, params: { merge_token: merge_token, email: email }, format: format }
|
||||
|
||||
|
@ -323,7 +323,7 @@ describe FranceConnect::ParticulierController, type: :controller do
|
|||
expect(fci.user.email).to eq(email.downcase.strip)
|
||||
expect(fci.merge_token).to be_nil
|
||||
expect(controller.current_user).to eq(fci.user)
|
||||
expect(response.body).to include("window.location.href='/'")
|
||||
expect(response).to redirect_to(root_path)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -83,7 +83,7 @@ describe 'France Connect Particulier Connexion' do
|
|||
|
||||
expect(page).to have_css('#password-for-another-account', visible: true)
|
||||
|
||||
within '.new-account-password-confirmation' do
|
||||
within '#new-account-password-confirmation' do
|
||||
fill_in 'password', with: 'my-s3cure-p4ssword'
|
||||
click_on 'Fusionner les comptes'
|
||||
end
|
||||
|
|
Loading…
Add table
Reference in a new issue