Merge pull request #8360 from mfo/US/dropdown_component

POC - amelioration(menu): extraction des menu dans un composant ruby pour ne pas dupliquer les changements aria partout ds la codebase
This commit is contained in:
mfo 2023-01-26 12:39:43 +01:00 committed by GitHub
commit 8af5ba434b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 425 additions and 381 deletions

View file

@ -44,6 +44,10 @@
position: relative;
}
.dropdown-export .dropdown-content {
width: 450px;
}
.print-menu {
display: none;
position: absolute;

View file

@ -1,21 +1,21 @@
%span.dropdown{ data: { controller: 'menu-button' } }
%button.fr-btn.fr-btn--sm.dropdown-button{ data: { menu_button_target: 'button' }, class: @class_btn.present? ? @class_btn : 'fr-btn--secondary' }
- if @count.nil?
= t(".download_all")
= render Dropdown::MenuComponent.new(wrapper: :span, button_options: { class: ['fr-btn--sm', @class_btn.present? ? @class_btn : 'fr-btn--secondary']}, menu_options: { id: @count.nil? ? "download_menu" : "download_all_menu", class: ['dropdown-export'] }) do |menu|
- menu.with_button_inner_html do
= @count.nil? ? t(".download_all") : t(".download", count: @count)
- exports.each do |item|
- export = item[:export]
- if export.nil?
- menu.with_item do
= link_to download_export_path(export_format: item[:format]), role: 'menuitem', data: { turbo_method: :post, turbo: true } do
= t(".everything_#{item[:format]}_html")
- elsif export.available?
- menu.with_item do
%div
= link_to ready_link_label(export), export.file.service_url, target: "_blank", rel: "noopener", role: 'menuitem'
- if export.old?
= button_to download_export_path(export_format: export.format, force_export: true), refresh_button_options(export).merge(role: 'menuitem') do
.icon.retry
- else
= t(".download", count: @count)
.dropdown-content.fade-in-down{ style: 'width: 450px', data: { menu_button_target: 'menu' }, id: @count.nil? ? "download_menu" : "download_all_menu" }
%ul.dropdown-items{ 'data-turbo': 'true' }
- exports.each do |item|
- export = item[:export]
%li
- if export.nil?
= link_to t(".everything_#{item[:format]}_html"), download_export_path(export_format: item[:format]), data: { turbo_method: :post }
- elsif export.available?
= link_to ready_link_label(export), export.file.service_url, target: "_blank", rel: "noopener"
- if export.old?
= button_to download_export_path(export_format: export.format, force_export: true), **refresh_button_options(export) do
.icon.retry
- else
%span{ data: poll_controller_options(export) }
= pending_label(export)
- menu.with_item(aria: {disabled:"true"}, class: 'selected') do
%span{ data: poll_controller_options(export) }
= pending_label(export)

View file

@ -0,0 +1,50 @@
class Dropdown::MenuComponent < ApplicationComponent
renders_one :button_inner_html
# beware, items elements like button_to/link_to must include role: 'menuitem' for aria reason
renders_many :items, -> (options = {}, &block) do
tag.li({ role: 'none' }.merge(options), &block)
end
renders_many :forms
def initialize(wrapper:,
wrapper_options: {},
button_options: {},
menu_options: {})
@wrapper = wrapper
@wrapper_options = wrapper_options
@button_options = button_options
@menu_options = menu_options
end
def wrapper_options
@wrapper_options.deep_merge({
class: wrapper_class_names,
data: { controller: 'menu-button' }
})
end
def wrapper_class_names
['dropdown'] + Array(@wrapper_options[:class])
end
def button_id
"#{menu_id}_button"
end
def menu_id
@menu_options[:id] ||= SecureRandom.uuid
@menu_options[:id]
end
def menu_role
forms? ? :region : :menu
end
def menu_class_names
['dropdown-content'] + Array(@menu_options[:class])
end
def button_class_names
['fr-btn', 'dropdown-button'] + Array(@button_options[:class])
end
end

View file

@ -0,0 +1,15 @@
= content_tag(@wrapper, wrapper_options) do
%button{ class: button_class_names, id: button_id, data: { menu_button_target: 'button' }, "aria-expanded": "false", 'aria-haspopup': 'true', 'aria-controls': menu_id }
= button_inner_html
%div{ data: { menu_button_target: 'menu' }, id: menu_id, 'aria-labelledby': button_id, role: menu_role, 'tab-index': -1, class: menu_class_names }
-# the dropdown can be a menu with a list of item
- if items?
%ul.dropdown-items.fr-pl-0{ role: 'none' }
- items.each do |dropdown_item|
= dropdown_item
-# the dropdown can be a menu with forms
- if forms?
- forms.each do |form|
= form

View file

@ -21,38 +21,17 @@ export class MenuButtonController extends ApplicationController {
}
private get isMenu() {
return !(this.element as HTMLElement).dataset.popover;
return this.menuTarget.getAttribute('role') == 'menu';
}
private setup() {
this.buttonTarget.setAttribute(
'aria-haspopup',
this.isMenu ? 'menu' : 'true'
);
this.buttonTarget.setAttribute('aria-controls', this.menuTarget.id);
if (!this.buttonTarget.id) {
this.buttonTarget.id = `${this.menuTarget.id}_button`;
}
this.menuTarget.setAttribute('aria-labelledby', this.buttonTarget.id);
this.menuTarget.setAttribute('role', this.isMenu ? 'menu' : 'region');
// see:
// To progressively enhance this navigation widget that is by default accessible,
// the class to hide the menu and the inclusion of tabindex="-1" on the interactive menuitem
// content should be added with JavaScript on load.
this.menuTarget.classList.add('fade-in-down');
this.menuTarget.setAttribute('tab-index', '-1');
if (this.isMenu) {
for (const menuItem of this.menuTarget.querySelectorAll('a')) {
menuItem.setAttribute('role', 'menuitem');
}
for (const dropdownItems of this.menuTarget.querySelectorAll(
'.dropdown-items'
)) {
dropdownItems.setAttribute('role', 'none');
}
for (const dropdownItems of this.menuTarget.querySelectorAll(
'.dropdown-items > li'
)) {
dropdownItems.setAttribute('role', 'none');
}
this.menuItems.map((menuItem) => menuItem.setAttribute('tabindex', '-1'));
}
this.on('click', (event) => {
@ -106,7 +85,7 @@ export class MenuButtonController extends ApplicationController {
}
private close() {
this.buttonTarget.removeAttribute('aria-expanded');
this.buttonTarget.setAttribute('aria-expanded', 'false');
this.menuTarget.parentElement?.classList.remove('open');
this.#teardown?.();
this.setFocusToMenuitem(null);

View file

@ -41,42 +41,43 @@
%li
= link_to admin_procedure_path(procedure), class: 'fr-btn fr-icon-draft-line fr-btn--tertiary' do
Modifier
%li.dropdown{ data: { controller: 'menu-button' } }
%button.fr-btn.fr-btn--tertiary.dropdown-button.procedures-actions-btn{ data: { menu_button_target: 'button' } }
= render Dropdown::MenuComponent.new(wrapper: :li, button_options: { class: ['fr-btn--tertiary'] }, menu_options: { id: dom_id(procedure, :actions_menu)}) do |menu|
- menu.with_button_inner_html do
Actions
.dropdown-content.fade-in-down{ data: { menu_button_target: 'menu' }, id: dom_id(procedure, :actions_menu) }
%ul.dropdown-items.pl-0
- if !procedure.close? && !procedure.discarded?
%li
= link_to sanitize_url(procedure.brouillon? ? commencer_test_url(path: procedure.path) : commencer_url(path: procedure.path)), target: :blank, rel: :noopener do
%span.icon.in-progress
.dropdown-description
%h4= t('administrateurs.dropdown_actions.to_test')
- unless procedure.discarded?
%li
= link_to admin_procedure_clone_path(procedure.id), class: 'clone-btn', data: { method: :put } do
%span.icon.new-folder
.dropdown-description
%h4= t('administrateurs.dropdown_actions.to_clone')
- if !procedure.close? && !procedure.discarded?
- menu.with_item do
= link_to(sanitize_url(procedure.brouillon? ? commencer_test_url(path: procedure.path) : commencer_url(path: procedure.path)), target: :blank, rel: :noopener, role: 'menuitem') do
%span.icon.in-progress
.dropdown-description
%h4= t('administrateurs.dropdown_actions.to_test')
- if procedure.publiee?
%li
= link_to admin_procedure_close_path(procedure_id: procedure.id) do
%span.icon.archive
.dropdown-description
%h4= t('administrateurs.dropdown_actions.to_close')
- if !procedure.discarded?
- menu.with_item do
= link_to(admin_procedure_clone_path(procedure.id), role: 'menuitem', class: 'clone-btn', data: { method: :put }) do
%span.icon.new-folder
.dropdown-description
%h4= t('administrateurs.dropdown_actions.to_clone')
- if procedure.can_be_deleted_by_administrateur? && !procedure.discarded?
%li
= link_to admin_procedure_path(procedure), method: :delete, data: { confirm: "Voulez-vous vraiment supprimer la démarche ? \nToute suppression est définitive et s'appliquera aux éventuels autres administrateurs de cette démarche !" } do
%span.icon.refuse
.dropdown-description
%h4= t('administrateurs.dropdown_actions.delete')
- if procedure.publiee?
- menu.with_item do
= link_to(admin_procedure_close_path(procedure_id: procedure.id), role: 'menuitem') do
%span.icon.archive
.dropdown-description
%h4= t('administrateurs.dropdown_actions.to_close')
- if procedure.can_be_deleted_by_administrateur? && !procedure.discarded?
- menu.with_item do
= link_to admin_procedure_path(procedure), role: 'menuitem', method: :delete, data: { confirm: "Voulez-vous vraiment supprimer la démarche ? \nToute suppression est définitive et s'appliquera aux éventuels autres administrateurs de cette démarche !" } do
%span.icon.refuse
.dropdown-description
%h4= t('administrateurs.dropdown_actions.delete')
- if procedure.discarded?
- menu.with_item do
= link_to restore_admin_procedure_path(procedure), role: 'menuitem', method: :put do
%span.icon.unarchive
.dropdown-description
%h4= t('administrateurs.dropdown_actions.restore')
- if procedure.discarded?
%li
= link_to restore_admin_procedure_path(procedure), method: :put do
%span.icon.unarchive
.dropdown-description
%h4= t('administrateurs.dropdown_actions.restore')

View file

@ -9,11 +9,11 @@
%li= link_to("Dossier nº #{dossier.id}", expert_avis_path(avis.procedure, avis))
.header-actions
%span.dropdown.print-menu-opener{ data: { controller: 'menu-button' } }
%button.button.dropdown-button.icon-only{ data: { menu_button_target: 'button' } }
%span.icon.attached
%ul.print-menu.dropdown-content#print-pj-menu{ data: { menu_button_target: 'menu' } }
%li= link_to "Télécharger le dossier et toutes ses pièces jointes", telecharger_pjs_expert_avis_path(avis.procedure, avis), target: "_blank", rel: "noopener", class: "menu-item menu-link"
.fr-download
= link_to telecharger_pjs_expert_avis_path(avis.procedure, avis), download: :download, class: "menu-item menu-link fr-download__link" do
Télécharger le dossier et toutes ses pièces jointes
%span.fr-download__detail
ZIP
%nav.tabs
%ul

View file

@ -1,21 +1,31 @@
%ul.fr-btns-group.fr-btns-group--sm.fr-btns-group--inline-md.fr-btns-group--icon-right
%li.dropdown.print-menu-opener{ data: { controller: 'menu-button' } }
%button.fr-btn.fr-btn--tertiary.fr-icon-printer-line.dropdown-button{ title: 'imprimer', 'aria-label': 'Imprimer', data: { menu_button_target: 'button' } } Imprimer
%ul#print-menu.print-menu.dropdown-content{ data: { menu_button_target: 'menu' } }
%li
= link_to "Tout le dossier", print_instructeur_dossier_path(dossier.procedure, dossier), target: "_blank", rel: "noopener", class: "menu-item menu-link"
%li
= link_to "Uniquement cet onglet", "#", onclick: "window.print()", class: "menu-item menu-link"
%li
= link_to "Export PDF", instructeur_dossier_path(dossier.procedure, dossier, format: :pdf), target: "_blank", rel: "noopener", class: "menu-item menu-link"
- if dossier.geo_data?
%li
= link_to "Export GeoJSON", geo_data_instructeur_dossier_path(dossier.procedure, dossier), target: "_blank", rel: "noopener", class: "menu-item menu-link"
= render Dropdown::MenuComponent.new(wrapper: :li, menu_options: { id: 'print-menu'}, button_options: { class: ['fr-btn--tertiary', 'fr-icon-printer-line']}) do |menu|
- menu.with_button_inner_html do
Imprimer
%li.dropdown.print-menu-opener{ data: { controller: 'menu-button' } }
%button.fr-btn.fr-btn--tertiary.fr-icon-download-line.dropdown-button{ data: { menu_button_target: 'button', 'aria-label': 'Télécharger' } } Télécharger
%ul#print-pj-menu.print-menu.dropdown-content{ data: { menu_button_target: 'menu' } }
%li= link_to "Télécharger le dossier et toutes ses pièces jointes", telecharger_pjs_instructeur_dossier_path(dossier.procedure, dossier), target: "_blank", rel: "noopener", class: "menu-item menu-link"
- menu.with_item do
= link_to print_instructeur_dossier_path(dossier.procedure, dossier), role: 'menuitem', target: "_blank", rel: "noopener", class: "menu-item menu-link" do
Tout le dossier
- menu.with_item do
= link_to '#', role: 'menuitem', onclick: "window.print()", class: "menu-item menu-link" do
Uniquement cet onglet
- menu.with_item do
= link_to instructeur_dossier_path(dossier.procedure, dossier, format: :pdf), target: "_blank", rel: "noopener", class: "menu-item menu-link", role: 'menuitem' do
Export PDF
- if dossier.geo_data?
- menu.with_item do
= link_to geo_data_instructeur_dossier_path(dossier.procedure, dossier), target: "_blank", rel: "noopener", class: "menu-item menu-link", role: 'menuitem' do
Export GeoJSON
= render Dropdown::MenuComponent.new(wrapper: :li, menu_options: { id: 'print-pj-menu'}, button_options: { class: ['fr-btn--tertiary', 'fr-icon-download-line']}) do |menu|
- menu.with_button_inner_html do
Télécharger
- menu.with_item do
= link_to telecharger_pjs_instructeur_dossier_path(dossier.procedure, dossier), target: "_blank", rel: "noopener", class: "menu-item menu-link", role: 'menuitem' do
Télécharger le dossier et toutes ses pièces jointes
%li
= render partial: "instructeurs/procedures/dossier_actions",

View file

@ -1,120 +1,107 @@
.dropdown{ data: { controller: 'menu-button', popover: 'true', turbo_force: true } }
-# Dropdown button title
%button.fr-btn.dropdown-button{ class: button_or_label_class(dossier), data: { menu_button_target: 'button' } }
= dossier_display_state dossier
= render Dropdown::MenuComponent.new(wrapper: :div, wrapper_options: { data: {'turbo-force': true} }, button_options: { class: [button_or_label_class(dossier)] }) do |menu|
- menu.with_button_inner_html do
= dossier_display_state(dossier)
-# Dropdown content
#state-menu.dropdown-content.fade-in-down{ data: { menu_button_target: 'menu' } }
- if dossier.en_construction?
- menu.with_item(aria: {disabled:"true"}, class: 'selected') do
%span.icon.edit
.dropdown-description
%h4 En construction
Vous permettez à l'usager de modifier ses réponses au formulaire
- if dossier.en_construction?
-# ------------------------------------------------------
-# EN CONSTRUCTION
-# ------------------------------------------------------
%ul.dropdown-items
- menu.with_item('data-turbo': 'true') do
= link_to(passer_en_instruction_instructeur_dossier_path(dossier.procedure, dossier), data: { turbo_method: :post, turbo_confirm: "Confirmez-vous le passage en instruction de ce dossier ?", turbo: true }, role: 'menuitem') do
%span.icon.in-progress
.dropdown-description
%h4 Passer en instruction
Lusager ne pourra plus modifier le formulaire
%li.selected
%span.icon.edit
- elsif dossier.en_instruction?
- menu.with_item('data-turbo': 'true') do
= link_to(repasser_en_construction_instructeur_dossier_path(dossier.procedure, dossier), data: { turbo_method: :post, turbo_confirm: "Confirmez-vous le passage en construction de ce dossier ?"}, role: 'menuitem') do
%span.icon.edit
.dropdown-description
%h4 Repasser en construction
Vous permettrez à l'usager de modifier ses réponses au formulaire
- menu.with_item(aria: {disabled:"true"}, class: 'selected') do
%span.icon.in-progress
.dropdown-description
%h4 En instruction
Lusager ne peut modifier son dossier pendant l'instruction
- menu.with_item do
= link_to('#', onclick: "DS.showMotivation(event, 'accept');", role: 'menuitem') do
%span.icon.accept
.dropdown-description
%h4 Accepter
Lusager sera notifié que son dossier a été accepté
- menu.with_item do
= link_to('#', onclick: "DS.showMotivation(event, 'without-continuation');", role: 'menuitem') do
%span.icon.without-continuation
.dropdown-description
%h4 Classer sans suite
Lusager sera notifié que son dossier a été classé sans suite
- menu.with_item do
= link_to('#', onclick: "DS.showMotivation(event, 'refuse');", role: 'menuitem') do
%span.icon.refuse
.dropdown-description
%h4 Refuser
Lusager sera notifié que son dossier a été refusé
- menu.with_form do
= render partial: 'instructeurs/dossiers/state_button_motivation', locals: { dossier: dossier, popup_title: 'Accepter le dossier', placeholder: 'Expliquez au demandeur pourquoi ce dossier est accepté (facultatif)', popup_class: 'accept', process_action: 'accepter', title: 'Accepter', confirm: "Confirmez-vous l'acceptation ce dossier ?" }
- menu.with_form do
= render partial: 'instructeurs/dossiers/state_button_motivation', locals: { dossier: dossier, popup_title: 'Classer le dossier sans suite', placeholder: 'Expliquez au demandeur pourquoi ce dossier est classé sans suite (obligatoire)', popup_class: 'without-continuation', process_action: 'classer_sans_suite', title: 'Classer sans suite', confirm: 'Confirmez-vous le classement sans suite de ce dossier ?' }
- menu.with_form do
= render partial: 'instructeurs/dossiers/state_button_motivation', locals: { dossier: dossier, popup_title: 'Refuser le dossier', placeholder: 'Expliquez au demandeur pourquoi ce dossier est refusé (obligatoire)', popup_class: 'refuse', process_action: 'refuser', title: 'Refuser', confirm: 'Confirmez-vous le refus de ce dossier ?' }
- elsif dossier.termine?
- if dossier.motivation.present?
- menu.with_item(class: 'inactive') do
%span.icon.info
.dropdown-description
%h4 Motivation
%p « #{dossier.motivation} »
- if dossier.justificatif_motivation.attached?
- menu.with_item(class: 'inactive') do
%span.icon.justificatif
.dropdown-description
%h4 Justificatif
%p Ce justificatif joint par linstructeur a été envoyé au demandeur.
= render partial: 'users/dossiers/show/download_justificatif', locals: { dossier: dossier }
- if dossier.attestation.present?
- menu.with_item do
= link_to(attestation_instructeur_dossier_path(dossier.procedure, dossier), target: '_blank', rel: 'noopener', role: 'menuitem') do
%span.icon.preview
.dropdown-description
%h4 En construction
Vous permettez à l'usager de modifier ses réponses au formulaire
%h4 Voir lattestation
%p Cette attestation a été envoyée automatiquement au demandeur.
%li{ 'data-turbo': 'true' }
= link_to passer_en_instruction_instructeur_dossier_path(dossier.procedure, dossier), data: { turbo_method: :post, turbo_confirm: "Confirmez-vous le passage en instruction de ce dossier ?" } do
%span.icon.in-progress
.dropdown-description
%h4 Passer en instruction
Lusager ne pourra plus modifier le formulaire
- elsif dossier.en_instruction?
-# ------------------------------------------------------
-# EN INSTRUCTION
-# ------------------------------------------------------
%ul.dropdown-items
%li{ 'data-turbo': 'true' }
= link_to repasser_en_construction_instructeur_dossier_path(dossier.procedure, dossier), data: { turbo_method: :post, turbo_confirm: "Confirmez-vous le passage en construction de ce dossier ?" } do
%span.icon.edit
.dropdown-description
%h4 Repasser en construction
Vous permettrez à l'usager de modifier ses réponses au formulaire
%li.selected
- if dossier.can_repasser_en_instruction?
- menu.with_item do
= link_to(repasser_en_instruction_instructeur_dossier_path(dossier.procedure, dossier), data: { turbo_method: :post, turbo_confirm: "Voulez vous remettre le dossier #{dossier.id} en instruction ?", turbo: true }, role: 'menuitem') do
%span.icon.in-progress
.dropdown-description
%h4 En instruction
Lusager ne peut modifier son dossier pendant l'instruction
%h4 Repasser en instruction
Lusager sera notifié que son dossier est réexaminé.
- elsif dossier.user_deleted?
- menu.with_item do
%span.icon.info
.dropdown-description
%h4 En attente darchivage
Lusager a supprimé son compte. Vous pouvez archiver puis supprimer le dossier.
%li
%a{ href: '#', onclick: "DS.showMotivation(event, 'accept');" }
%span.icon.accept
.dropdown-description
%h4 Accepter
Lusager sera notifié que son dossier a été accepté
%li
%a{ href: '#', onclick: "DS.showMotivation(event, 'without-continuation');" }
%span.icon.without-continuation
.dropdown-description
%h4 Classer sans suite
Lusager sera notifié que son dossier a été classé sans suite
%li
%a{ href: '#', onclick: "DS.showMotivation(event, 'refuse');" }
%span.icon.refuse
.dropdown-description
%h4 Refuser
Lusager sera notifié que son dossier a été refusé
= render partial: 'instructeurs/dossiers/state_button_motivation', locals: { dossier: dossier, popup_title: 'Accepter le dossier', placeholder: 'Expliquez au demandeur pourquoi ce dossier est accepté (facultatif)', popup_class: 'accept', process_action: 'accepter', title: 'Accepter', confirm: "Confirmez-vous l'acceptation ce dossier ?" }
= render partial: 'instructeurs/dossiers/state_button_motivation', locals: { dossier: dossier, popup_title: 'Classer le dossier sans suite', placeholder: 'Expliquez au demandeur pourquoi ce dossier est classé sans suite (obligatoire)', popup_class: 'without-continuation', process_action: 'classer_sans_suite', title: 'Classer sans suite', confirm: 'Confirmez-vous le classement sans suite de ce dossier ?' }
= render partial: 'instructeurs/dossiers/state_button_motivation', locals: { dossier: dossier, popup_title: 'Refuser le dossier', placeholder: 'Expliquez au demandeur pourquoi ce dossier est refusé (obligatoire)', popup_class: 'refuse', process_action: 'refuser', title: 'Refuser', confirm: 'Confirmez-vous le refus de ce dossier ?' }
- elsif dossier.termine?
-# ---------------------------------------------------
-# TERMINÉ
-# ---------------------------------------------------
%ul.dropdown-items
- if dossier.motivation.present?
%li.inactive
%span.icon.info
.dropdown-description
%h4 Motivation
%p « #{dossier.motivation} »
- if dossier.justificatif_motivation.attached?
%li.inactive
%span.icon.justificatif
.dropdown-description
%h4 Justificatif
%p Ce justificatif joint par linstructeur a été envoyé au demandeur.
= render partial: 'users/dossiers/show/download_justificatif', locals: { dossier: dossier }
- if dossier.attestation.present?
%li
= link_to attestation_instructeur_dossier_path(dossier.procedure, dossier), target: '_blank', rel: 'noopener' do
%span.icon.preview
.dropdown-description
%h4 Voir lattestation
%p Cette attestation a été envoyée automatiquement au demandeur.
- if dossier.can_repasser_en_instruction?
%li{ 'data-turbo': 'true' }
= link_to repasser_en_instruction_instructeur_dossier_path(dossier.procedure, dossier), data: { turbo_method: :post, turbo_confirm: "Voulez vous remettre le dossier #{dossier.id} en instruction ?" } do
%span.icon.in-progress
.dropdown-description
%h4 Repasser en instruction
Lusager sera notifié que son dossier est réexaminé.
- elsif dossier.user_deleted?
%li
%span.icon.info
.dropdown-description
%h4 En attente darchivage
Lusager a supprimé son compte. Vous pouvez archiver puis supprimer le dossier.
%li
= link_to instructeur_dossier_path(dossier.procedure, dossier), method: :delete do
%span.icon.delete
.dropdown-description
%h4 Supprimer le dossier
- menu.with_item do
= link_to(instructeur_dossier_path(dossier.procedure, dossier), method: :delete, role: 'menuitem') do
%span.icon.delete
.dropdown-description
%h4 Supprimer le dossier

View file

@ -2,33 +2,36 @@
= link_to restore_instructeur_dossier_path(procedure_id, dossier_id), method: :patch, class: "fr-btn fr-btn--secondary" do
= t('views.instructeurs.dossiers.restore')
- elsif close_to_expiration || Dossier::TERMINE.include?(state)
%li.dropdown.user-dossier-actions{ data: { controller: 'menu-button' } }
%button.fr-btn.fr-mb-0.dropdown-button{ data: { menu_button_target: 'button' } }
= render Dropdown::MenuComponent.new(wrapper: :li, button_options: { class: ['fr-mb-0']}, menu_options: { id: "dossier_#{dossier_id}_actions_menu", class: 'user-dossier-actions' }) do |menu|
- menu.with_button_inner_html do
Actions
.dropdown-content.fade-in-down{ data: { menu_button_target: 'menu' }, id: "dossier_#{dossier_id}_actions_menu" }
%ul.dropdown-items
- if close_to_expiration
%li
= link_to repousser_expiration_instructeur_dossier_path(procedure_id, dossier_id), method: :post do
%span.icon.standby
%span.dropdown-description= t('instructeurs.dossiers.header.banner.button_delay_expiration')
- if archived
%li
= link_to unarchive_instructeur_dossier_path(procedure_id, dossier_id), method: :patch do
%span.icon.unarchive
%span.dropdown-description
Désarchiver le dossier
- else
%li
= link_to archive_instructeur_dossier_path(procedure_id, dossier_id), method: :patch do
%span.icon.archive
%span.dropdown-description
Archiver le dossier
%li.danger
= link_to instructeur_dossier_path(procedure_id, dossier_id), method: :delete do
%span.icon.delete
%span.dropdown-description
= t('views.instructeurs.dossiers.delete_dossier')
- if close_to_expiration
- menu.with_item do
= link_to(repousser_expiration_instructeur_dossier_path(procedure_id, dossier_id), method: :post, role: 'menuitem') do
%span.icon.standby
%span.dropdown-description= t('instructeurs.dossiers.header.banner.button_delay_expiration')
- if archived
- menu.with_item do
= link_to( unarchive_instructeur_dossier_path(procedure_id, dossier_id), role: 'menuitem', method: :patch) do
%span.icon.unarchive
%span.dropdown-description
Désarchiver le dossier
- else
- menu.with_item do
= link_to( archive_instructeur_dossier_path(procedure_id, dossier_id), method: :patch, role: 'menuitem') do
%span.icon.archive
%span.dropdown-description
Archiver le dossier
- menu.with_item(class: 'danger') do
= link_to(instructeur_dossier_path(procedure_id, dossier_id), method: :delete, role: 'menuitem') do
%span.icon.delete
%span.dropdown-description
= t('views.instructeurs.dossiers.delete_dossier')
- elsif Dossier::EN_CONSTRUCTION_OU_INSTRUCTION.include?(state)
- if dossier_is_followed

View file

@ -1,5 +1,7 @@
%span.dropdown{ data: { controller: 'menu-button', popover: 'true' } }
%button.fr-btn.fr-btn--tertiary.fr-btn--sm.fr-mr-2w.dropdown-button{ data: { menu_button_target: 'button' } }
= render Dropdown::MenuComponent.new(wrapper: :div, button_options: { class: ['fr-btn--secondary', 'fr-btn--sm', 'fr-mr-1w'] }, menu_options: { id: 'filter-menu', class:['left-aligned'] }) do |menu|
- menu.with_button_inner_html do
= t('views.instructeurs.dossiers.filters.title')
#filter-menu.dropdown-content.left-aligned.fade-in-down{ data: { menu_button_target: 'menu' } }
- menu.with_form do
= render Dossiers::FilterComponent.new(procedure: procedure, procedure_presentation: @procedure_presentation, statut: statut)

View file

@ -62,10 +62,10 @@
= render Dossiers::NotifiedToggleComponent.new(procedure: @procedure, procedure_presentation: @procedure_presentation)
.fr-ml-auto
%span.dropdown{ data: { controller: 'menu-button', popover: 'true' } }
%button.fr-btn.fr-btn--sm.fr-btn--tertiary.dropdown-button.fr-ml-1w{ data: { menu_button_target: 'button' } }
= render Dropdown::MenuComponent.new(wrapper: :span, button_options: { class: ['fr-btn--sm', 'fr-btn--secondary'] }, menu_options: { id: 'custom-menu' }) do |menu|
- menu.with_button_inner_html do
= t('views.instructeurs.dossiers.personalize')
#custom-menu.dropdown-content.fade-in-down{ data: { menu_button_target: 'menu' } }
- menu.with_form do
= form_tag update_displayed_fields_instructeur_procedure_path(@procedure), method: :patch, class: 'dropdown-form large columns-form' do
= hidden_field_tag :values, nil
= react_component("ComboMultiple",
@ -120,6 +120,7 @@
%th.action-col.follow-col
Actions
%tr
%tbody

View file

@ -1,6 +1,6 @@
- invites = dossier.invites.load
.dropdown.invite-user-action{ data: { controller: 'menu-button', popover: 'true' } }
%button.button.dropdown-button{ data: { menu_button_target: 'button' } }
= render Dropdown::MenuComponent.new(wrapper: :span, wrapper_options: {class: 'invite-user-action'}, button_options: { class: ['fr-btn--secondary'] }, menu_options: { id: 'invite-content' }) do |menu|
- menu.with_button_inner_html do
%span.icon.person
- if invites.present?
= t('views.invites.dropdown.view_invited_people')
@ -10,6 +10,5 @@
= t('views.invites.dropdown.invite_to_view')
- else
= t('views.invites.dropdown.invite_to_edit')
#invite-content.dropdown-content.fade-in-down{ data: { menu_button_target: 'menu' } }
- menu.with_form do
= render partial: "invites/form", locals: { dossier: dossier, invites: invites }

View file

@ -67,21 +67,21 @@
- if instructeur_dossier && expert_dossier
%td.action-col.follow-col
.dropdown{ data: { controller: 'menu-button' } }
%button.button.dropdown-button{ data: { menu_button_target: 'button' } }
= render Dropdown::MenuComponent.new(wrapper: :div, button_options: {class: ['fr-btn--sm']}) do |menu|
- menu.with_button_inner_html do
Actions
.dropdown-content{ data: { menu_button_target: 'menu' } }
%ul.dropdown-items
%li
= link_to(instructeur_dossier_path(procedure_id, p.dossier_id)) do
%span.icon.in-progress>
.dropdown-description
Voir le dossier
%li
= link_to(expert_avis_path(procedure_id, @dossier_avis_ids_h[p.dossier_id])) do
%span.icon.in-progress>
.dropdown-description
Donner mon avis
- menu.with_item do
= link_to(instructeur_dossier_path(procedure_id, p.dossier_id), role: 'menuitem') do
%span.icon.in-progress>
.dropdown-description
Voir le dossier
- menu.with_item do
= link_to(expert_avis_path(procedure_id, @dossier_avis_ids_h[p.dossier_id]), role: 'menuitem') do
%span.icon.in-progress>
.dropdown-description
Donner mon avis
- elsif instructeur_dossier
- if hidden_by_administration

View file

@ -1,15 +1,16 @@
.dropdown.help-dropdown{ data: { controller: 'menu-button' } }
%button.fr-btn.dropdown-button{ data: { menu_button_target: 'button' } }
= render Dropdown::MenuComponent.new(wrapper: :span, wrapper_options: { class: ['help-dropdown']}, menu_options: { id: "help-menu" }) do |menu|
- menu.with_button_inner_html do
= t('help')
#help-menu.dropdown-content.fade-in-down{ data: { menu_button_target: 'menu' } }
%ul.dropdown-items
- title = dossier.brouillon? ? "Besoin daide pour remplir votre dossier ?" : "Une question sur votre dossier ?"
- title = dossier.brouillon? ? "Besoin daide pour remplir votre dossier ?" : "Une question sur votre dossier ?"
- if dossier.messagerie_available?
= render partial: 'shared/help/dropdown_items/messagerie_item',
locals: { dossier: dossier, title: title }
- elsif dossier.procedure.service.present?
= render partial: 'shared/help/dropdown_items/service_item',
locals: { service: dossier.procedure.service, title: title }
- if dossier.messagerie_available?
- menu.with_item do
= render partial: 'shared/help/dropdown_items/messagerie_item', locals: { dossier: dossier, title: title }
= render partial: 'shared/help/dropdown_items/faq_item'
- elsif dossier.procedure.service.present?
- menu.with_item do
= render partial: 'shared/help/dropdown_items/service_item',
locals: { service: dossier.procedure.service, title: title }
- menu.with_item do
= render partial: 'shared/help/dropdown_items/faq_item'

View file

@ -1,7 +1,8 @@
.dropdown.help-dropdown{ data: { controller: 'menu-button' } }
%button.fr-btn.dropdown-button{ data: { menu_button_target: 'button' } }
= render Dropdown::MenuComponent.new(wrapper: :span, wrapper_options: { class: ['help-dropdown']}, menu_options: { id: "help-menu" }) do |menu|
- menu.with_button_inner_html do
= t('help')
#help-menu.dropdown-content.fade-in-down{ data: { menu_button_target: 'menu' } }
%ul.dropdown-items
= render partial: 'shared/help/dropdown_items/faq_item'
= render partial: 'shared/help/dropdown_items/email_item'
- menu.with_item do
= render partial: 'shared/help/dropdown_items/faq_item'
- menu.with_item do
= render partial: 'shared/help/dropdown_items/email_item'

View file

@ -1,10 +1,9 @@
.dropdown.help-dropdown{ data: { controller: 'menu-button' } }
%button.fr-btn.dropdown-button{ data: { menu_button_target: 'button' } }
= render Dropdown::MenuComponent.new(wrapper: :span, wrapper_options: { class: ['help-dropdown']}, menu_options: { id: "help-menu" }) do |menu|
- menu.with_button_inner_html do
= t('help')
#help-menu.dropdown-content.fade-in-down{ data: { menu_button_target: 'menu' } }
%ul.dropdown-items
- if procedure.service.present?
= render partial: 'shared/help/dropdown_items/service_item',
locals: { service: procedure.service, title: "Une question sur cette démarche ?" }
= render partial: 'shared/help/dropdown_items/faq_item'
- if procedure.service.present?
- menu.with_item do
= render partial: 'shared/help/dropdown_items/service_item', locals: { service: procedure.service, title: "Une question sur cette démarche ?" }
- menu.with_item do
= render partial: 'shared/help/dropdown_items/faq_item'

View file

@ -1,5 +1,5 @@
%li
= mail_to CONTACT_EMAIL do
%li{ role: 'none' }
= mail_to CONTACT_EMAIL, role: 'menuitem' do
%span.icon.mail
.dropdown-description
%span.help-dropdown-title

View file

@ -1,9 +1,7 @@
%li
= link_to t("links.common.faq.url"), title: new_tab_suffix(t('help_dropdown.general_title')), **external_link_attributes do
%span.icon.help
.dropdown-description
%span.help-dropdown-title
= t('help_dropdown.problem_title')
%p
= t('help_dropdown.problem_description')
= link_to t("links.common.faq.url"), title: new_tab_suffix(t('help_dropdown.general_title')), **external_link_attributes, role: 'menuitem' do
%span.icon.help
.dropdown-description
%span.help-dropdown-title
= t('help_dropdown.problem_title')
%p
= t('help_dropdown.problem_description')

View file

@ -1,6 +1,5 @@
%li
= link_to messagerie_dossier_path(dossier) do
%span.icon.mail
.dropdown-description
%span.help-dropdown-title= title
%p Envoyez directement un message à linstructeur.
= link_to messagerie_dossier_path(dossier), role: 'menuitem' do
%span.icon.mail
.dropdown-description
%span.help-dropdown-title= title
%p Envoyez directement un message à linstructeur.

View file

@ -1,15 +1,14 @@
%li.help-dropdown-service
%span.icon.person
.dropdown-description
%span.help-dropdown-title= title
.help-dropdown-service-action
%p Contactez directement ladministration :
%p.help-dropdown-service-item
%span.icon.small.mail
= link_to service.email, "mailto:#{service.email}"
%p.help-dropdown-service-item
%span.icon.small.phone
= link_to service.telephone, service.telephone_url
%p.help-dropdown-service-item
%span.icon.small.clock
= service.horaires
%span.icon.person
.dropdown-description
%span.help-dropdown-title= title
.help-dropdown-service-action
%p Contactez directement ladministration :
%p.help-dropdown-service-item
%span.icon.small.mail
= link_to service.email, "mailto:#{service.email}", role: 'menuitem'
%p.help-dropdown-service-item
%span.icon.small.phone
= link_to service.telephone, service.telephone_url, role: 'menuitem'
%p.help-dropdown-service-item
%span.icon.small.clock
= service.horaires

View file

@ -4,49 +4,51 @@
- has_transfer_action = dossier.user == current_user
- has_actions = has_edit_action || has_delete_action || has_new_dossier_action || has_transfer_action
- if has_actions
.dropdown.user-dossier-actions{ data: { controller: 'menu-button' } }
%button.fr-btn.fr-btn--secondary.dropdown-button{ data: { menu_button_target: 'button' } }
= render Dropdown::MenuComponent.new(wrapper: :div, wrapper_options: {class: 'invite-user-actions'}, menu_options: {id: dom_id(dossier, :actions_menu)}, button_options: {class: 'fr-btn--sm fr-btn--secondary'}) do |menu|
- menu.with_button_inner_html do
= t('views.users.dossiers.dossier_action.actions')
.dropdown-content.fade-in-down{ data: { menu_button_target: 'menu' }, id: dom_id(dossier, :actions_menu) }
%ul.dropdown-items
- if has_edit_action
- if dossier.brouillon?
%li
= link_to(url_for_dossier(dossier)) do
%span.icon.edit
.dropdown-description
= t('views.users.dossiers.dossier_action.edit_draft')
- else
%li
= link_to modifier_dossier_path(dossier) do
%span.icon.edit
.dropdown-description
= t('views.users.dossiers.dossier_action.edit_dossier')
- if has_transfer_action
%li
= link_to transferer_dossier_path(dossier) do
%span.icon.person
.dropdown-description
= t('views.users.dossiers.dossier_action.transfer_dossier')
- if has_edit_action
- if dossier.brouillon?
- menu.with_item do
= link_to(url_for_dossier(dossier), role: 'menuitem') do
%span.icon.edit
.dropdown-description
= t('views.users.dossiers.dossier_action.edit_draft')
- else
- menu.with_item do
= link_to(modifier_dossier_path(dossier), role: 'menuitem') do
%span.icon.edit
.dropdown-description
= t('views.users.dossiers.dossier_action.edit_dossier')
- if has_new_dossier_action
%li
= link_to procedure_lien(dossier.procedure) do
%span.icon.new-folder
.dropdown-description
= t('views.users.dossiers.dossier_action.start_other_dossier')
%li
= link_to clone_dossier_path(dossier), method: :post do
%span.icon.new-folder
.dropdown-description
= t('views.users.dossiers.dossier_action.clone')
- if has_transfer_action
- menu.with_item do
= link_to(transferer_dossier_path(dossier), role: 'menuitem') do
%span.icon.person
.dropdown-description
= t('views.users.dossiers.dossier_action.transfer_dossier')
- if has_delete_action
%li.danger
= link_to delete_dossier_dossier_path(dossier), method: :patch, data: { disable: true, confirm: "En continuant, vous allez supprimer ce dossier ainsi que les informations quil contient. Toute suppression entraîne lannulation de la démarche en cours.\n\nConfirmer la suppression ?" } do
%span.icon.delete
.dropdown-description
= t('views.users.dossiers.dossier_action.delete_dossier')
- if has_new_dossier_action
- menu.with_item do
= link_to(procedure_lien(dossier.procedure), role: 'menuitem') do
%span.icon.new-folder
.dropdown-description
= t('views.users.dossiers.dossier_action.start_other_dossier')
- menu.with_item do
= link_to(clone_dossier_path(dossier), method: :post, role: 'menuitem') do
%span.icon.new-folder
.dropdown-description
= t('views.users.dossiers.dossier_action.clone')
- if has_delete_action
- menu.with_item(class: 'danger') do
= link_to(delete_dossier_dossier_path(dossier), role: 'menuitem', method: :patch, data: { disable: true, confirm: "En continuant, vous allez supprimer ce dossier ainsi que les informations quil contient. Toute suppression entraîne lannulation de la démarche en cours.\n\nConfirmer la suppression ?" }) do
%span.icon.delete
.dropdown-description
= t('views.users.dossiers.dossier_action.delete_dossier')

View file

@ -1,8 +1,8 @@
.dropdown.edit-identity-action{ data: { controller: 'menu-button', popover: 'true' } }
%button.button.dropdown-button{ data: { menu_button_target: 'button' } }
= render Dropdown::MenuComponent.new(wrapper: :div, wrapper_options: {class: ['edit-identity-action']}, menu_options: { class:['edit-identity-content'] }) do |menu|
- menu.with_button_inner_html do
= t("views.shared.dossiers.demande.my_identity")
#edit-identity-content.dropdown-content.fade-in-down{ data: { menu_button_target: 'menu' } }
- menu.with_form do
- if dossier.procedure.for_individual
= render partial: "shared/dossiers/identite_individual", locals: { individual: dossier.individual }

View file

@ -10,5 +10,5 @@
.container
- if !@dossier.read_only?
= link_to t('views.users.dossiers.demande.edit_dossier'), modifier_dossier_path(@dossier), class: 'button accepted edit-form', title: "Modifier mon dossier tant qu'il n'est pas passé en instruction"
= link_to t('views.users.dossiers.demande.edit_dossier'), modifier_dossier_path(@dossier), class: 'fr-btn fr-btn-sm', 'title'=> "Modifier mon dossier tant qu'il n'est pas passé en instruction"
.clearfix

View file

@ -18,7 +18,7 @@
.header-actions
= render partial: 'invites/dropdown', locals: { dossier: dossier }
- if dossier.can_be_updated_by_user? && !current_page?(modifier_dossier_path(dossier))
= link_to t('views.users.dossiers.show.header.edit_dossier'), modifier_dossier_path(dossier), class: 'button accepted edit-form',
= link_to t('views.users.dossiers.show.header.edit_dossier'), modifier_dossier_path(dossier), class: 'fr-btn fr-btn-sm',
title: { label: t('views.users.dossiers.show.header.edit_dossier_title') }
= render(partial: 'users/dossiers/show/print_dossier', locals: { dossier: dossier })

View file

@ -1,6 +1 @@
.dropdown.print-menu-opener{ data: { controller: 'menu-button' } }
%button.button.dropdown-button.icon-only{ title: t('views.users.dossiers.show.header.print'), 'aria-label': 'imprimer', data: { menu_button_target: 'button' } }
%span.icon.printer
%ul#print-menu.print-menu.dropdown-content{ data: { menu_button_target: 'menu' } }
%li
= link_to t('views.users.dossiers.show.header.print_dossier'), dossier_path(dossier, format: :pdf), target: "_blank", rel: "noopener", class: "menu-item menu-link"
= link_to t('views.users.dossiers.show.header.print'), dossier_path(dossier, format: :pdf), target: "_blank", rel: "noopener", title: t('views.users.dossiers.show.header.print_dossier'), class: 'fr-btn fr-icon-printer-line fr-btn--tertiary'

View file

@ -18,7 +18,7 @@ describe 'As an administrateur I wanna clone a procedure', js: true do
scenario do
visit admin_procedures_path
expect(page.find_by_id('procedures')['data-item-count']).to eq('1')
page.all('.procedures-actions-btn').first.click
page.all('.admin-procedures-list-row .dropdown .fr-btn').first.click
page.all('.clone-btn').first.click
visit admin_procedures_path(statut: "brouillons")
expect(page.find_by_id('procedures')['data-item-count']).to eq('1')

View file

@ -116,7 +116,6 @@ describe 'Inviting an expert:' do
click_on '1 avis à donner'
click_on avis.dossier.user.email
find(:css, '[aria-controls=print-pj-menu]').click
click_on 'Télécharger le dossier et toutes ses pièces jointes'
# For some reason, clicking the download link does not trigger the download in the headless browser ;
# So we need to go to the download link directly

View file

@ -27,7 +27,7 @@ describe 'users/dossiers/show/header.html.haml', type: :view do
end
it 'can download the dossier' do
expect(rendered).to have_text("Tout le dossier")
expect(rendered).to have_selector('a[title="Tout le dossier"]')
end
end
@ -45,7 +45,7 @@ describe 'users/dossiers/show/header.html.haml', type: :view do
end
it 'can download the dossier' do
expect(rendered).to have_text("Tout le dossier")
expect(rendered).to have_selector('a[title="Tout le dossier"]')
end
it 'does not display a new procedure link' do
@ -68,7 +68,7 @@ describe 'users/dossiers/show/header.html.haml', type: :view do
end
it 'can download the dossier' do
expect(rendered).to have_text("Tout le dossier")
expect(rendered).to have_selector('a[title="Tout le dossier"]')
end
it 'displays a new procedure link' do
@ -105,7 +105,7 @@ describe 'users/dossiers/show/header.html.haml', type: :view do
end
it 'can download the dossier' do
expect(rendered).to have_text("Tout le dossier")
expect(rendered).to have_selector('a[title="Tout le dossier"]')
end
end
@ -124,7 +124,7 @@ describe 'users/dossiers/show/header.html.haml', type: :view do
end
it 'can not download the dossier' do
expect(rendered).not_to have_text("Tout le dossier")
expect(rendered).not_to have_selector('a[title="Tout le dossier"]')
end
end
end