diff --git a/app/assets/stylesheets/new_design/avis.scss b/app/assets/stylesheets/new_design/avis.scss index 51930f4bc..0217f3d76 100644 --- a/app/assets/stylesheets/new_design/avis.scss +++ b/app/assets/stylesheets/new_design/avis.scss @@ -65,7 +65,6 @@ } .confidentiel-explanation { - display: none; font-size: 14px; color: $grey; margin-top: -$default-padding; diff --git a/app/assets/stylesheets/new_design/motivation.scss b/app/assets/stylesheets/new_design/motivation.scss index 52781f9b2..5adcbc78b 100644 --- a/app/assets/stylesheets/new_design/motivation.scss +++ b/app/assets/stylesheets/new_design/motivation.scss @@ -2,7 +2,6 @@ @import "constants"; .motivation { - display: none; padding: $default-padding; color: $black; width: 450px; diff --git a/app/javascript/new_design/avis.js b/app/javascript/new_design/avis.js index c35fdc6eb..99b4f869c 100644 --- a/app/javascript/new_design/avis.js +++ b/app/javascript/new_design/avis.js @@ -1,5 +1,5 @@ -import $ from 'jquery'; +import { toggle } from '@utils'; export function toggleCondidentielExplanation() { - $('.confidentiel-explanation').toggle(); + toggle(document.querySelector('.confidentiel-explanation')); } diff --git a/app/javascript/new_design/carto.js b/app/javascript/new_design/carto.js index 11fbbe75d..895a613ba 100644 --- a/app/javascript/new_design/carto.js +++ b/app/javascript/new_design/carto.js @@ -1,5 +1,5 @@ -import $ from 'jquery'; import L from 'leaflet'; +import { getJSON } from '@utils'; import { getData } from '../shared/data'; import { DEFAULT_POSITION } from '../shared/carto'; @@ -11,8 +11,8 @@ import { } from './carto/draw'; function initialize() { - if ($('#map').length > 0) { - $.getJSON(getData('carto').getPositionUrl).then( + if (document.getElementById('map')) { + getJSON(getData('carto').getPositionUrl).then( position => initializeWithPosition(position), () => initializeWithPosition(DEFAULT_POSITION) ); diff --git a/app/javascript/new_design/dropdown.js b/app/javascript/new_design/dropdown.js index 2829b98d8..13d8965c2 100644 --- a/app/javascript/new_design/dropdown.js +++ b/app/javascript/new_design/dropdown.js @@ -1,8 +1,6 @@ -import Rails from 'rails-ujs'; +import { delegate } from '@utils'; -const { delegate } = Rails; - -delegate(document, 'body', 'click', event => { +delegate('click', 'body', event => { if (!event.target.closest('.dropdown')) { [...document.querySelectorAll('.dropdown')].forEach(element => element.classList.remove('open', 'fade-in-down') @@ -10,7 +8,7 @@ delegate(document, 'body', 'click', event => { } }); -delegate(document, '.dropdown-button', 'click', event => { +delegate('click', '.dropdown-button', event => { event.stopPropagation(); const parent = event.target.closest('.dropdown-button').parentElement; if (parent.classList.contains('dropdown')) { diff --git a/app/javascript/new_design/form-validation.js b/app/javascript/new_design/form-validation.js index 06b9c161c..9f0b74184 100644 --- a/app/javascript/new_design/form-validation.js +++ b/app/javascript/new_design/form-validation.js @@ -1,10 +1,19 @@ -import $ from 'jquery'; +import { delegate } from '@utils'; -$(document).on('blur keydown', 'input, textarea', () => { - $(this).addClass('touched'); +delegate('blur keydown', 'input, textarea', ({ target }) => { + touch(target); }); -$(document).on('click', 'input[type="submit"]:not([formnovalidate])', () => { - const $form = $(this).closest('form'); - $('input, textarea', $form).addClass('touched'); -}); +delegate( + 'click', + 'input[type="submit"]:not([formnovalidate])', + ({ target }) => { + let form = target.closest('form'); + let inputs = form ? form.querySelectorAll('input, textarea') : []; + [...inputs].forEach(touch); + } +); + +function touch({ classList }) { + classList.add('touched'); +} diff --git a/app/javascript/new_design/messagerie.js b/app/javascript/new_design/messagerie.js index a7dd2bc39..91e9190db 100644 --- a/app/javascript/new_design/messagerie.js +++ b/app/javascript/new_design/messagerie.js @@ -1,27 +1,17 @@ -import $ from 'jquery'; +import { scrollTo, scrollToBottom } from '@utils'; export function scrollMessagerie() { - const $ul = $('.messagerie ul').first(); + const ul = document.querySelector('.messagerie ul'); - if ($ul.length) { - const $elementToScroll = $('.date.highlighted').first(); + if (ul) { + const elementToScroll = document.querySelector('.date.highlighted'); - if ($elementToScroll.length != 0) { - scrollTo($ul, $elementToScroll); + if (elementToScroll) { + scrollTo(ul, elementToScroll); } else { - scrollToBottom($ul); + scrollToBottom(ul); } } } -function scrollTo($container, $scrollTo) { - $container.scrollTop( - $scrollTo.offset().top - $container.offset().top + $container.scrollTop() - ); -} - -function scrollToBottom($container) { - $container.scrollTop($container.prop('scrollHeight')); -} - addEventListener('turbolinks:load', scrollMessagerie); diff --git a/app/javascript/new_design/spinner.js b/app/javascript/new_design/spinner.js index 7c92ca53b..8b1fbac3e 100644 --- a/app/javascript/new_design/spinner.js +++ b/app/javascript/new_design/spinner.js @@ -1,7 +1,4 @@ -import Rails from 'rails-ujs'; -import { show, hide } from '../shared/utils'; - -const { delegate } = Rails; +import { show, hide, delegate } from '@utils'; function showSpinner() { [...document.querySelectorAll('.spinner')].forEach(show); @@ -11,6 +8,6 @@ function hideSpinner() { [...document.querySelectorAll('.spinner')].forEach(hide); } -delegate(document, '[data-spinner]', 'ajax:complete', hideSpinner); -delegate(document, '[data-spinner]', 'ajax:stopped', hideSpinner); -delegate(document, '[data-spinner]', 'ajax:send', showSpinner); +delegate('ajax:complete', '[data-spinner]', hideSpinner); +delegate('ajax:stopped', '[data-spinner]', hideSpinner); +delegate('ajax:send', '[data-spinner]', showSpinner); diff --git a/app/javascript/new_design/state-button.js b/app/javascript/new_design/state-button.js index 0f0983a41..da47340fd 100644 --- a/app/javascript/new_design/state-button.js +++ b/app/javascript/new_design/state-button.js @@ -1,12 +1,12 @@ -import $ from 'jquery'; +import { show, hide } from '@utils'; export function showMotivation(event, state) { event.preventDefault(); - $(`.motivation.${state}`).show(); - $('.dropdown-items').hide(); + show(document.querySelector(`.motivation.${state}`)); + hide(document.querySelector('.dropdown-items')); } export function motivationCancel() { - $('.motivation').hide(); - $('.dropdown-items').show(); + document.querySelectorAll('.motivation').forEach(hide); + show(document.querySelector('.dropdown-items')); } diff --git a/app/javascript/new_design/toggle-chart.js b/app/javascript/new_design/toggle-chart.js index b51622402..2de8b6394 100644 --- a/app/javascript/new_design/toggle-chart.js +++ b/app/javascript/new_design/toggle-chart.js @@ -1,28 +1,24 @@ -import $ from 'jquery'; import Chartkick from 'chartkick'; +import { toggle } from '@utils'; export function toggleChart(event, chartClass) { - const nextSelectorItem = $(event.target), - nextChart = $(chartClass), - nextChartId = nextChart - .children() - .first() - .attr('id'), - currentSelectorItem = nextSelectorItem - .parent() - .find('.segmented-control-item-active'), - currentChart = nextSelectorItem - .parent() - .parent() - .find('.chart:not(.hidden)'); + const nextSelectorItem = event.target, + nextChart = document.querySelector(chartClass), + nextChartId = nextChart.children[0].id, + currentSelectorItem = nextSelectorItem.parentElement.querySelector( + '.segmented-control-item-active' + ), + currentChart = nextSelectorItem.parentElement.parentElement.querySelector( + '.chart:not(.hidden)' + ); // Change the current selector and the next selector states - currentSelectorItem.toggleClass('segmented-control-item-active'); - nextSelectorItem.toggleClass('segmented-control-item-active'); + currentSelectorItem.classList.toggle('segmented-control-item-active'); + nextSelectorItem.classList.toggle('segmented-control-item-active'); // Hide the currently shown chart and show the new one - currentChart.toggleClass('hidden'); - nextChart.toggleClass('hidden'); + toggle(currentChart); + toggle(nextChart); // Reflow needed, see https://github.com/highcharts/highcharts/issues/1979 Chartkick.charts[nextChartId].getChartObject().reflow(); diff --git a/app/javascript/packs/application.js b/app/javascript/packs/application.js index 758b934fa..530b94bdf 100644 --- a/app/javascript/packs/application.js +++ b/app/javascript/packs/application.js @@ -4,7 +4,6 @@ import Rails from 'rails-ujs'; import ActiveStorage from '../shared/activestorage/ujs'; import Chartkick from 'chartkick'; import Highcharts from 'highcharts'; -import jQuery from 'jquery'; import '../shared/sentry'; import '../shared/rails-ujs-fix'; @@ -41,11 +40,6 @@ Rails.start(); Turbolinks.start(); ActiveStorage.start(); -// Disable jQuery-driven animations during tests -if (process.env['RAILS_ENV'] === 'test') { - jQuery.fx.off = true; -} - // Expose globals window.DS = window.DS || DS; window.Chartkick = Chartkick; diff --git a/app/javascript/shared/autocomplete.js b/app/javascript/shared/autocomplete.js index 727ae172a..809373bf1 100644 --- a/app/javascript/shared/autocomplete.js +++ b/app/javascript/shared/autocomplete.js @@ -1,5 +1,5 @@ -import $ from 'jquery'; import autocomplete from 'autocomplete.js'; +import { getJSON, fire } from '@utils'; const sources = [ { @@ -24,7 +24,7 @@ function selector(type) { function source(url) { return { source(query, callback) { - $.getJSON(url, { request: query }).then(callback); + getJSON(url, { request: query }).then(callback); }, templates: { suggestion({ label, mine }) { @@ -41,7 +41,7 @@ addEventListener('turbolinks:load', function() { for (let target of document.querySelectorAll(selector(type))) { let select = autocomplete(target, options, [source(url)]); select.on('autocomplete:selected', ({ target }, suggestion) => { - $(target).trigger('autocomplete:select', suggestion); + fire(target, 'autocomplete:select', suggestion); select.autocomplete.setVal(suggestion.label); }); } diff --git a/app/javascript/shared/rails-ujs-fix.js b/app/javascript/shared/rails-ujs-fix.js index a7cc7eb5e..f36330901 100644 --- a/app/javascript/shared/rails-ujs-fix.js +++ b/app/javascript/shared/rails-ujs-fix.js @@ -1,12 +1,13 @@ import Rails from 'rails-ujs'; import jQuery from 'jquery'; +import { delegate } from '@utils'; // We use `jQuery.active` in our capybara suit to wait for ajax requests. // Newer jQuery-less version of rails-ujs is breaking it. // We have to set `ajax:complete` listener on the same element as the one // we catch ajax:send on as by the end of the request // the old element may be removed from DOM. -Rails.delegate(document, '[data-remote]', 'ajax:send', ({ target }) => { +delegate('ajax:send', '[data-remote]', ({ target }) => { let callback = () => { jQuery.active--; target.removeEventListener('ajax:complete', callback); diff --git a/app/javascript/shared/remote-input.js b/app/javascript/shared/remote-input.js index d03e69640..e265a1835 100644 --- a/app/javascript/shared/remote-input.js +++ b/app/javascript/shared/remote-input.js @@ -1,7 +1,4 @@ -import Rails from 'rails-ujs'; -import debounce from 'debounce'; - -const { delegate, fire } = Rails; +import { delegate, fire, debounce } from '@utils'; const remote = 'data-remote'; const inputChangeSelector = `input[${remote}], textarea[${remote}]`; @@ -21,4 +18,4 @@ function isRemote(element) { return value && value !== 'false'; } -delegate(document, inputChangeSelector, 'input', debounce(handleRemote, 200)); +delegate('input', inputChangeSelector, debounce(handleRemote, 200)); diff --git a/app/javascript/shared/utils.js b/app/javascript/shared/utils.js index 9339dd07f..9b413e98b 100644 --- a/app/javascript/shared/utils.js +++ b/app/javascript/shared/utils.js @@ -1,3 +1,10 @@ +import Rails from 'rails-ujs'; +import $ from 'jquery'; +import debounce from 'debounce'; + +export { debounce }; +export const { fire } = Rails; + export function show({ classList }) { classList.remove('hidden'); } @@ -9,3 +16,45 @@ export function hide({ classList }) { export function toggle({ classList }) { classList.toggle('hidden'); } + +export function delegate(eventNames, selector, callback) { + eventNames + .split(' ') + .forEach(eventName => + Rails.delegate(document, selector, eventName, callback) + ); +} + +export function getJSON(url, data, method = 'get') { + data = method !== 'get' ? JSON.stringify(data) : data; + return $.ajax({ + method, + url, + data, + contentType: 'application/json', + dataType: 'json' + }); +} + +export function scrollTo(container, scrollTo) { + container.scrollTop = + offset(scrollTo).top - offset(container).top + container.scrollTop; +} + +export function scrollToBottom(container) { + container.scrollTop = container.scrollHeight; +} + +export function on(selector, eventName, fn) { + [...document.querySelectorAll(selector)].forEach(element => + element.addEventListener(eventName, event => fn(event, event.detail)) + ); +} + +function offset(element) { + const rect = element.getBoundingClientRect(); + return { + top: rect.top + document.body.scrollTop, + left: rect.left + document.body.scrollLeft + }; +} diff --git a/app/views/new_gestionnaire/dossiers/_state_button_motivation.html.haml b/app/views/new_gestionnaire/dossiers/_state_button_motivation.html.haml index d72c793e7..99a6dfc88 100644 --- a/app/views/new_gestionnaire/dossiers/_state_button_motivation.html.haml +++ b/app/views/new_gestionnaire/dossiers/_state_button_motivation.html.haml @@ -1,4 +1,4 @@ -.motivation{ class: popup_class } +.motivation.hidden{ class: popup_class } %h3 %span.icon{ class: popup_class } #{popup_title} diff --git a/app/views/new_gestionnaire/shared/avis/_form.html.haml b/app/views/new_gestionnaire/shared/avis/_form.html.haml index c429b8103..29aefebfa 100644 --- a/app/views/new_gestionnaire/shared/avis/_form.html.haml +++ b/app/views/new_gestionnaire/shared/avis/_form.html.haml @@ -17,7 +17,7 @@ .confidentiel-wrapper = f.label :confidentiel, 'Cet avis est' = f.select :confidentiel, [['partagé avec les autres experts', false], ['confidentiel', true]], {}, onchange: "javascript:DS.toggleCondidentielExplanation(event);" - .confidentiel-explanation + .confidentiel-explanation.hidden Il ne sera pas affiché aux autres experts consultés mais sera visible par les instructeurs .send-wrapper = f.submit 'Demander un avis', class: 'button send' diff --git a/config/webpack/environment.js b/config/webpack/environment.js index d261e8336..6016319bc 100644 --- a/config/webpack/environment.js +++ b/config/webpack/environment.js @@ -1,3 +1,4 @@ +const path = require('path'); const { environment } = require('@rails/webpacker'); // By default don't transpile JS files in ./node_modules – except for some specific modules. @@ -14,4 +15,12 @@ babelLoader.exclude = function(modulePath) { ); }; +const resolve = { + alias: { + '@utils': path.resolve(__dirname, '..', '..', 'app/javascript/shared/utils') + } +}; + +environment.config.merge({ resolve }); + module.exports = environment;