diff --git a/app/assets/javascripts/admin/utils/directives/help-modal.js.coffee b/app/assets/javascripts/admin/utils/directives/help-modal.js.coffee deleted file mode 100644 index 9dd8eda04ba3..000000000000 --- a/app/assets/javascripts/admin/utils/directives/help-modal.js.coffee +++ /dev/null @@ -1,20 +0,0 @@ -angular.module("admin.utils").directive 'helpModal', ($rootScope, $compile, $templateCache, $window, DialogDefaults) -> - restrict: 'C' - scope: - template: '@' - link: (scope, element, attr) -> - # Compile modal template - template = $compile($templateCache.get(scope.template))(scope) - - # Load Dialog Options - template.dialog(DialogDefaults) - - # Link opening of dialog to click event on element - element.bind 'click', (e) -> - template.dialog('open') - $rootScope.$evalAsync() - - scope.close = -> - template.dialog('close') - $rootScope.$evalAsync() - return diff --git a/app/assets/javascripts/darkswarm/directives/help_modal.js.coffee b/app/assets/javascripts/darkswarm/directives/help_modal.js.coffee deleted file mode 100644 index 69e730315cb0..000000000000 --- a/app/assets/javascripts/darkswarm/directives/help_modal.js.coffee +++ /dev/null @@ -1,13 +0,0 @@ -angular.module('Darkswarm').directive "helpModal", ($modal, $compile, $templateCache)-> - restrict: 'A' - scope: - helpText: "@helpModal" - - link: (scope, elem, attrs, ctrl)-> - compiled = $compile($templateCache.get('help-modal.html'))(scope) - - elem.on "click", => - $modal.open(controller: ctrl, template: compiled, scope: scope, windowClass: 'help-modal small') - - scope.$on "$destroy", -> - elem.off("click") diff --git a/app/assets/javascripts/templates/admin/modals/business_address_info.html.haml b/app/assets/javascripts/templates/admin/modals/business_address_info.html.haml deleted file mode 100644 index 9a0945e90c1f..000000000000 --- a/app/assets/javascripts/templates/admin/modals/business_address_info.html.haml +++ /dev/null @@ -1,7 +0,0 @@ -%div - .margin-bottom-30 - %p - {{ 'js.admin.modals.business_address_info.message' | t }} - - .text-center - %input.button.red.icon-plus{ type: 'button', value: '{{ "js.admin.modals.got_it" | t }}', ng: { click: 'close()' } } diff --git a/app/assets/javascripts/templates/admin/modals/invite_manager.html.haml b/app/assets/javascripts/templates/admin/modals/invite_manager.html.haml deleted file mode 100644 index bbde3baa4959..000000000000 --- a/app/assets/javascripts/templates/admin/modals/invite_manager.html.haml +++ /dev/null @@ -1,19 +0,0 @@ -#invite-manager-modal{ng: {app: 'admin.enterprises', controller: 'enterpriseCtrl'}} - - .margin-bottom-30.text-center - .text-big - = t('js.admin.modals.invite_title') - - %p.alert-box.ok{ng: {show: 'invite_success'}} - {{invite_success}} - - %p.alert-box.error{ng: {show: 'invite_errors'}} - {{invite_errors}} - - %input#invite_email.fullwidth.margin-bottom-20{ng: {model: 'newUser'}} - - .margin-bottom-20.text-center - %button.text-center.margin-top-10{ng: {show: '!invite_success', click: 'inviteManager()'}} - = t('js.admin.modals.invite') - %button.text-center.margin-top-10{ng: {show: 'invite_success', click: 'resetModal(); close()'}} - = t('js.admin.modals.close') diff --git a/app/assets/javascripts/templates/admin/modals/tag_rule_help.html.haml b/app/assets/javascripts/templates/admin/modals/tag_rule_help.html.haml deleted file mode 100644 index 57e82a4d3473..000000000000 --- a/app/assets/javascripts/templates/admin/modals/tag_rule_help.html.haml +++ /dev/null @@ -1,25 +0,0 @@ -#tag-rule-help - .margin-bottom-30.text-center - .text-big - {{ 'js.admin.modals.tag_rule_help.title' | t }} - - .margin-bottom-30 - .text-normal - {{ 'js.admin.modals.tag_rule_help.overview' | t }} - %p - {{ 'js.admin.modals.tag_rule_help.overview_text' | t }} - - .margin-bottom-30 - .text-normal - {{ 'js.admin.modals.tag_rule_help.by_default_rules' | t }} - %p - {{ 'js.admin.modals.tag_rule_help.by_default_rules_text' | t }} - - .margin-bottom-30 - .text-normal - {{ 'js.admin.modals.tag_rule_help.customer_tagged_rules' | t }} - %p - {{ 'js.admin.modals.tag_rule_help.customer_tagged_rules_text' | t }} - - .text-center - %input.button.red.icon-plus{ type: 'button', value: t('js.admin.modals.got_it'), ng: { click: 'close()' } } diff --git a/app/assets/javascripts/templates/admin/modals/terms_and_conditions_info.html.haml b/app/assets/javascripts/templates/admin/modals/terms_and_conditions_info.html.haml deleted file mode 100644 index 5a3568085ed6..000000000000 --- a/app/assets/javascripts/templates/admin/modals/terms_and_conditions_info.html.haml +++ /dev/null @@ -1,13 +0,0 @@ -%div - .margin-bottom-30.text-center - .text-big - {{ 'js.admin.modals.terms_and_conditions_info.title' | t }} - .margin-bottom-30 - %p - {{ 'js.admin.modals.terms_and_conditions_info.message_1' | t }} - .margin-bottom-30 - %p - {{ 'js.admin.modals.terms_and_conditions_info.message_2' | t }} - - .text-center - %input.button.red.icon-plus{ type: 'button', value: t('js.admin.modals.got_it'), ng: { click: 'close()' } } diff --git a/app/components/help_modal_component.rb b/app/components/help_modal_component.rb new file mode 100644 index 000000000000..9e4d1ca6bdb6 --- /dev/null +++ b/app/components/help_modal_component.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +class HelpModalComponent < ViewComponent::Base + def initialize(id:, close_button: true) + @id = id + @close_button = close_button + end + + private + + def close_button_class + if namespace == "admin" + "red" + else + "primary" + end + end + + def close_button? + !!@close_button + end + + def namespace + helpers.controller_path.split("/").first + end +end diff --git a/app/components/help_modal_component/help_modal_component.html.haml b/app/components/help_modal_component/help_modal_component.html.haml new file mode 100644 index 000000000000..af7d60272421 --- /dev/null +++ b/app/components/help_modal_component/help_modal_component.html.haml @@ -0,0 +1,8 @@ +%div{ id: @id, "data-controller": "help-modal", "data-action": "keyup@document->help-modal#closeIfEscapeKey" } + .reveal-modal-bg.fade{ "data-help-modal-target": "background", "data-action": "click->help-modal#close" } + .reveal-modal.fade.small.help-modal{ "data-help-modal-target": "modal" } + = content + + - if close_button? + .text-center + %input{ class: "button icon-plus #{close_button_class}", type: 'button', value: t('js.admin.modals.got_it'), "data-action": "click->help-modal#close" } diff --git a/app/components/help_modal_component/help_modal_component.scss b/app/components/help_modal_component/help_modal_component.scss new file mode 100644 index 000000000000..48f6255a2e7c --- /dev/null +++ b/app/components/help_modal_component/help_modal_component.scss @@ -0,0 +1,10 @@ +.help-modal { + visibility: visible; + position: fixed; + top: 3em; +} + +/* prevent arrow on selected admin menu item appearing above modal */ +body.modal-open #admin-menu li.selected a::after { + z-index: 0; +} diff --git a/app/views/admin/enterprises/form/_business_address.html.haml b/app/views/admin/enterprises/form/_business_address.html.haml index be99ce6cfd19..069c838baa6a 100644 --- a/app/views/admin/enterprises/form/_business_address.html.haml +++ b/app/views/admin/enterprises/form/_business_address.html.haml @@ -1,15 +1,14 @@ .row .three.columns.alpha = bf.label :company, t(".company_legal_name") - %i.text-big.icon-question-sign.help-modal{ template: 'admin/modals/business_address_info.html' } + %i.text-big.icon-question-sign{ "data-controller": "help-modal-link", "data-action": "click->help-modal-link#open", "data-help-modal-link-target-value": "business_address_info_modal" } .eight.columns.omega = bf.text_field :company, { placeholder: t(".company_placeholder") } .row .three.columns.alpha = bf.label :address1, t('.address1') - %i.text-big.icon-question-sign.help-modal{ template: 'admin/modals/business_address_info.html' } - .eight.columns.omega + %i.text-big.icon-question-sign{ "data-controller": "help-modal-link", "data-action": "click->help-modal-link#open", "data-help-modal-link-target-value": "business_address_info_modal" } = bf.text_field :address1, { placeholder: t(".address1_placeholder") } .row .alpha.three.columns @@ -40,7 +39,11 @@ .row .three.columns.alpha = bf.label :phone, t(".legal_phone_number") - %i.text-big.icon-question-sign.help-modal{ template: 'admin/modals/business_address_info.html' } + %i.text-big.icon-question-sign{ "data-controller": "help-modal-link", "data-action": "click->help-modal-link#open", "data-help-modal-link-target-value": "business_address_info_modal" } .eight.columns.omega = bf.text_field :phone, { placeholder: t(".phone_placeholder") } += render HelpModalComponent.new(id: "business_address_info_modal") do + .margin-bottom-30 + = t('js.admin.modals.business_address_info.message') + diff --git a/app/views/admin/enterprises/form/_business_details.html.haml b/app/views/admin/enterprises/form/_business_details.html.haml index 9c3481adce95..15e9a1f4c408 100644 --- a/app/views/admin/enterprises/form/_business_details.html.haml +++ b/app/views/admin/enterprises/form/_business_details.html.haml @@ -35,7 +35,7 @@ .row .alpha.three.columns = f.label :terms_and_conditions, t('.terms_and_conditions') - %i.text-big.icon-question-sign.help-modal{ template: 'admin/modals/terms_and_conditions_info.html' } + %i.text-big.icon-question-sign{ "data-controller": "help-modal-link", "data-action": "click->help-modal-link#open", "data-help-modal-link-target-value": "terms_and_conditions_info_modal" } .omega.eight.columns %a{ href: '{{ Enterprise.terms_and_conditions }}', target: '_blank', ng: { if: 'Enterprise.terms_and_conditions' } } @@ -69,5 +69,16 @@ = f.radio_button :preferred_invoice_order_by_supplier, true, 'ng-model' => 'Enterprise.preferred_invoice_order_by_supplier', 'ng-value' => 'true' = f.label :preffered_invoice_order_by_supplier, t('.enabled'), value: :true .five.columns.omega - = f.radio_button :preferred_invoice_order_by_supplier, false, 'ng-model' => 'Enterprise.preferred_invoice_order_by_supplier', 'ng-value' => 'false' + = f.radio_button :preferred_invoice_order_by_supplier, false, 'ng-model' => 'Enterprise.preferred_invoice_order_by_supplier', 'ng-value' => 'false' = f.label :preferred_invoice_order_by_name, t('.disabled'), value: :false + += render HelpModalComponent.new(id: "terms_and_conditions_info_modal") do + .margin-bottom-30.text-center + .text-big + = t('js.admin.modals.terms_and_conditions_info.title') + .margin-bottom-30 + %p + = t('js.admin.modals.terms_and_conditions_info.message_1') + .margin-bottom-30 + %p + = t('js.admin.modals.terms_and_conditions_info.message_2') diff --git a/app/views/admin/enterprises/form/_stripe_connect.html.haml b/app/views/admin/enterprises/form/_stripe_connect.html.haml index be0f4dd15723..5c4249971862 100644 --- a/app/views/admin/enterprises/form/_stripe_connect.html.haml +++ b/app/views/admin/enterprises/form/_stripe_connect.html.haml @@ -1,4 +1,3 @@ -= render 'admin/enterprises/form/stripe_connect/confirm_modal' - if @stripe_account = @enterprise.stripe_account .stripe-info @@ -11,5 +10,8 @@ .six.columns.alpha =t('.stripe_connect_intro') .five.columns.omega.text-right - %a.stripe-connect.help-modal{ template: 'admin/modals/stripe_connect_confirm.html' } + %a.stripe-connect{ "data-controller": "help-modal-link", "data-action": "click->help-modal-link#open", "data-help-modal-link-target-value": "stripe_connect_confirm_modal" } %span= t('.connect_with_stripe') + + = render HelpModalComponent.new(id: "stripe_connect_confirm_modal", close_button: false) do + = render 'admin/enterprises/form/stripe_connect/confirm_modal' diff --git a/app/views/admin/enterprises/form/_users.html.haml b/app/views/admin/enterprises/form/_users.html.haml index 9e75d65ecafe..863de9c8bf96 100644 --- a/app/views/admin/enterprises/form/_users.html.haml +++ b/app/views/admin/enterprises/form/_users.html.haml @@ -70,5 +70,28 @@ %a= t('admin.whats_this') .eight.columns.omega .row - %a.button.help-modal{template: 'admin/modals/invite_manager.html'} + %a.button{ "data-controller": "help-modal-link", "data-action": "click->help-modal-link#open", "data-help-modal-link-target-value": "invite-manager-modal" } = t('.add_unregistered_user') + +-# add to admin footer to avoid nesting invitation form inside enterprise form +- content_for :admin_footer do + = render HelpModalComponent.new(id: "invite-manager-modal", close_button: false) do + %div{ng: {app: 'admin.enterprises', controller: 'enterpriseCtrl'}} + + .margin-bottom-30.text-center + .text-big + = t('js.admin.modals.invite_title') + + %p.alert-box.ok{ng: {show: 'invite_success'}} + {{invite_success}} + + %p.alert-box.error{ng: {show: 'invite_errors'}} + {{invite_errors}} + + %input#invite_email.fullwidth.margin-bottom-20{ng: {model: 'newUser'}} + + .margin-bottom-20.text-center + %button.text-center.margin-top-10{ng: {show: '!invite_success', click: 'inviteManager()'}} + = t('js.admin.modals.invite') + %button.text-center.margin-top-10{"data-action": "click->help-modal#close", ng: {show: 'invite_success', click: 'resetModal();'}} + = t('js.admin.modals.close') diff --git a/app/views/admin/enterprises/form/stripe_connect/_confirm_modal.html.haml b/app/views/admin/enterprises/form/stripe_connect/_confirm_modal.html.haml index ac16e619a542..6dd1454fadfe 100644 --- a/app/views/admin/enterprises/form/stripe_connect/_confirm_modal.html.haml +++ b/app/views/admin/enterprises/form/stripe_connect/_confirm_modal.html.haml @@ -1,27 +1,26 @@ -%script{ type: "text/ng-template", id: "admin/modals/stripe_connect_confirm.html" } - -# Recommended info to impart (from Stripe Connect docs): - -# indicate to the user what you’re responsible for and what they’ll be expected to do. It’s particularly important to communicate: - -# That they’ll need to create and maintain their Stripe account. - -# That they’ll need to handle chargebacks and all customer service issues. - -# Who is responsible for paying the Stripe fees. - -# What, if any, fees the platform charges. +-# Recommended info to impart (from Stripe Connect docs): +-# indicate to the user what you’re responsible for and what they’ll be expected to do. It’s particularly important to communicate: +-# That they’ll need to create and maintain their Stripe account. +-# That they’ll need to handle chargebacks and all customer service issues. +-# Who is responsible for paying the Stripe fees. +-# What, if any, fees the platform charges. - #stripe-connect-confirm - .margin-bottom-30.text-center - .text-big - = t('.title') +#stripe-connect-confirm + .margin-bottom-30.text-center + .text-big + = t('.title') - .margin-bottom-30 - %p= t('.part1') + .margin-bottom-30 + %p= t('.part1') - .margin-bottom-30 - %p= t('.part2') + .margin-bottom-30 + %p= t('.part2') - .margin-bottom-30 - %p= t('.part3') + .margin-bottom-30 + %p= t('.part3') - .text-center - %a.button.icon-ok{ href: main_app.connect_admin_stripe_accounts_path(enterprise_id: @enterprise) } - = t('.i_agree') - %a.button.red.icon-remove{ href: 'javascript:void(0)', ng: { click: 'close()' } } - = t('.cancel') + .text-center + %a.button.icon-ok{ href: main_app.connect_admin_stripe_accounts_path(enterprise_id: @enterprise) } + = t('.i_agree') + %a.button.red.icon-remove{ "data-action": "click->help-modal#close" } + = t('.cancel') diff --git a/app/views/admin/enterprises/form/tag_rules/_default_rules.html.haml b/app/views/admin/enterprises/form/tag_rules/_default_rules.html.haml index a6e149d238d7..a58a71d8dec9 100644 --- a/app/views/admin/enterprises/form/tag_rules/_default_rules.html.haml +++ b/app/views/admin/enterprises/form/tag_rules/_default_rules.html.haml @@ -7,9 +7,34 @@ %td %h5 = t('.by_default') - %i.text-big.icon-question-sign.help-modal{ template: 'admin/modals/tag_rule_help.html' } + %i.text-big.icon-question-sign{ "data-controller": "help-modal-link", "data-action": "click->help-modal-link#open", "data-help-modal-link-target-value": "tag_rule_help_modal" } .no_rules{ ng: { show: "defaultTagGroup.rules.length == 0" } } = t('.no_rules_yet') .tag_rule{ ng: { repeat: "rule in defaultTagGroup.rules" } } .add_rule.text-center %input.button.icon-plus{ type: 'button', value: t('.add_new_button'), "add-new-rule-to" => "addNewRuleTo", "tag-group" => "defaultTagGroup", "new-tag-rule-dialog" => true } + += render HelpModalComponent.new(id: "tag_rule_help_modal") do + #tag-rule-help + .margin-bottom-30.text-center + .text-big + = t('js.admin.modals.tag_rule_help.title') + + .margin-bottom-30 + .text-normal + = t('js.admin.modals.tag_rule_help.overview') + %p + = t('js.admin.modals.tag_rule_help.overview_text') + + .margin-bottom-30 + .text-normal + = t('js.admin.modals.tag_rule_help.by_default_rules') + %p + = t('js.admin.modals.tag_rule_help.by_default_rules_text') + + .margin-bottom-30 + .text-normal + = t('js.admin.modals.tag_rule_help.customer_tagged_rules') + %p + = t('js.admin.modals.tag_rule_help.customer_tagged_rules_text') + diff --git a/app/views/spree/layouts/_admin_body.html.haml b/app/views/spree/layouts/_admin_body.html.haml index bc380b803c50..056b027fba32 100644 --- a/app/views/spree/layouts/_admin_body.html.haml +++ b/app/views/spree/layouts/_admin_body.html.haml @@ -71,3 +71,5 @@ = raw "Spree.api_key = \"#{spree_current_user.try(:spree_api_key).to_s}\";" = render "layouts/matomo_tag" + += yield :admin_footer diff --git a/app/views/spree/users/_cards.html.haml b/app/views/spree/users/_cards.html.haml index ea933674056d..869b59242a38 100644 --- a/app/views/spree/users/_cards.html.haml +++ b/app/views/spree/users/_cards.html.haml @@ -4,7 +4,7 @@ .small-12.medium-6.columns %h3 = t(:saved_cards) - %button.button.secondary.tiny.help-btn.ng-scope{ "help-modal" => t('.saved_cards_popover') } + %button.button.secondary.tiny.help-btn{ "data-controller": "help-modal-link", "data-action": "click->help-modal-link#open", "data-help-modal-link-target-value": "saved_cards_modal" } %i.ofn-i_013-help .saved_cards{ ng: { show: 'savedCreditCards.length > 0' } } @@ -22,3 +22,9 @@ %h3 = t('.authorised_shops') = render 'authorised_shops' + + = render HelpModalComponent.new(id: "saved_cards_modal") do + %p.text-center.text-vbig + %i.ofn-i_013-help + %p + = t('.saved_cards_popover') diff --git a/app/webpacker/controllers/help_modal_controller.js b/app/webpacker/controllers/help_modal_controller.js new file mode 100644 index 000000000000..eac046c68e30 --- /dev/null +++ b/app/webpacker/controllers/help_modal_controller.js @@ -0,0 +1,33 @@ +import { Controller } from "stimulus" + +export default class extends Controller { + static targets = ["background", "modal"] + + open() { + this.backgroundTarget.style.display = "block" + this.modalTarget.style.display = "block" + + setTimeout(() => { + this.modalTarget.classList.add("in") + this.backgroundTarget.classList.add("in") + document.querySelector("body").classList.add("modal-open") + }) + } + + close() { + this.modalTarget.classList.remove("in") + this.backgroundTarget.classList.remove("in") + document.querySelector("body").classList.remove("modal-open") + + setTimeout(() => { + this.backgroundTarget.style.display = "none" + this.modalTarget.style.display = "none" + }, 200) + } + + closeIfEscapeKey(e) { + if (e.code == "Escape") { + this.close() + } + } +} diff --git a/app/webpacker/controllers/help_modal_link_controller.js b/app/webpacker/controllers/help_modal_link_controller.js new file mode 100644 index 000000000000..e21febbbff1f --- /dev/null +++ b/app/webpacker/controllers/help_modal_link_controller.js @@ -0,0 +1,11 @@ +import { Controller } from "stimulus" + +export default class extends Controller { + static values = { target: String } + + open() { + let helpModal = document.getElementById(this.targetValue) + let helpModalController = this.application.getControllerForElementAndIdentifier(helpModal, "help-modal"); + helpModalController.open(); + } +} diff --git a/app/webpacker/css/admin/all.scss b/app/webpacker/css/admin/all.scss index abc0b785da91..d904a0f019f0 100644 --- a/app/webpacker/css/admin/all.scss +++ b/app/webpacker/css/admin/all.scss @@ -115,3 +115,5 @@ @import "../shared/question-mark-icon"; @import "question-mark-tooltip"; + +@import 'app/components/help_modal_component/help_modal_component'; diff --git a/app/webpacker/css/darkswarm/all.scss b/app/webpacker/css/darkswarm/all.scss index 803fcbe2a6c1..3738d945c6ec 100644 --- a/app/webpacker/css/darkswarm/all.scss +++ b/app/webpacker/css/darkswarm/all.scss @@ -35,7 +35,6 @@ @import 'footer'; @import 'forms'; @import 'groups'; -@import 'help-modal'; @import 'home_panes'; @import 'home_tagline'; @import 'hub_node'; @@ -76,3 +75,5 @@ ofn-modal { @import "../shared/question-mark-icon"; @import '../admin/shared/scroll_bar'; + +@import 'app/components/help_modal_component/help_modal_component'; diff --git a/app/webpacker/css/darkswarm/help-modal.scss b/app/webpacker/css/darkswarm/help-modal.scss deleted file mode 100644 index c26b17edc2d5..000000000000 --- a/app/webpacker/css/darkswarm/help-modal.scss +++ /dev/null @@ -1,9 +0,0 @@ -.help-modal { - .help-text { - font-size: 1rem; - margin: 20px 0px; - } - .help-icon { - font-size: 4rem; - } -} diff --git a/spec/javascripts/stimulus/help_modal_controller_test.js b/spec/javascripts/stimulus/help_modal_controller_test.js new file mode 100644 index 000000000000..1120ede69532 --- /dev/null +++ b/spec/javascripts/stimulus/help_modal_controller_test.js @@ -0,0 +1,103 @@ +/** + * @jest-environment jsdom + */ + +import { Application } from "stimulus"; +import help_modal_controller from "../../../app/webpacker/controllers/help_modal_controller"; +import help_modal_link_controller from "../../../app/webpacker/controllers/help_modal_link_controller"; + +expect.extend({ + toBeVisible(element) { + if(element.className.includes("in") && element.style.display == "block") { + return { pass: true } + } else { + return { pass: false } + } + }, +}); + +describe("HelpModalController", () => { + beforeAll(() => { + const application = Application.start(); + application.register("help-modal", help_modal_controller); + application.register("help-modal-link", help_modal_link_controller); + jest.useFakeTimers() + }); + + beforeEach(() => { + document.body.innerHTML = ` +