Merge pull request #9040 from cillian/stimulus-js-help-modals

Switch help modals from angular templates to use ViewComponent and StimulusJs
This commit is contained in:
Maikel
2022-04-20 12:14:41 +10:00
committed by GitHub
23 changed files with 299 additions and 140 deletions

View File

@@ -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

View File

@@ -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")

View File

@@ -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()' } }

View File

@@ -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')

View File

@@ -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()' } }

View File

@@ -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()' } }

View File

@@ -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

View File

@@ -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" }

View File

@@ -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;
}

View File

@@ -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')

View File

@@ -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')

View File

@@ -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'

View File

@@ -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')

View File

@@ -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 youre responsible for and what theyll be expected to do. Its particularly important to communicate:
-# That theyll need to create and maintain their Stripe account.
-# That theyll 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 youre responsible for and what theyll be expected to do. Its particularly important to communicate:
-# That theyll need to create and maintain their Stripe account.
-# That theyll 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')

View File

@@ -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')

View File

@@ -71,3 +71,5 @@
= raw "Spree.api_key = \"#{spree_current_user.try(:spree_api_key).to_s}\";"
= render "layouts/matomo_tag"
= yield :admin_footer

View File

@@ -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')

View File

@@ -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()
}
}
}

View File

@@ -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();
}
}

View File

@@ -118,3 +118,5 @@
@import "~tom-select/src/scss/tom-select.default";
@import "components/tom_select";
@import 'app/components/help_modal_component/help_modal_component';

View File

@@ -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';

View File

@@ -1,9 +0,0 @@
.help-modal {
.help-text {
font-size: 1rem;
margin: 20px 0px;
}
.help-icon {
font-size: 4rem;
}
}

View File

@@ -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 = `
<div id="help-modal-controller" data-controller="help-modal"
data-action="keyup@document->help-modal#closeIfEscapeKey">
<div id="background"
class="reveal-modal-bg.fade"
data-help-modal-target="background"
data-action="click->help-modal#close">
</div>
<div id="modal"
class="reveal-modal.fade.medium.help-modal"
data-help-modal-target="modal">
Hello world
<a id="close-link" data-action="click->help-modal#close">Close</a>
</div>
</div>
<a id="open-link"
data-controller="help-modal-link"
data-action="click->help-modal-link#open"
data-help-modal-link-target-value="help-modal-controller">
Open
</a>
`;
});
it("opens and closes", () => {
const modal = document.getElementById("modal");
const openLink = document.getElementById("open-link");
const closeLink = document.getElementById("close-link");
expect(document.body.className).not.toContain("modal-open")
expect(background).not.toBeVisible()
expect(modal).not.toBeVisible()
openLink.click();
jest.runAllTimers();
expect(document.body.className).toContain("modal-open")
expect(background).toBeVisible()
expect(modal).toBeVisible()
closeLink.click();
jest.runAllTimers();
expect(document.body.className).not.toContain("modal-open")
expect(background).not.toBeVisible()
expect(modal).not.toBeVisible()
});
it("closes when the escape key is pressed", () => {
const modal = document.getElementById("modal");
const openLink = document.getElementById("open-link");
openLink.click();
jest.runAllTimers()
expect(modal).toBeVisible()
document.dispatchEvent(new KeyboardEvent('keyup', { 'code': 'Escape' }));
jest.runAllTimers()
expect(modal).not.toBeVisible()
});
it("closes when the background is clicked", () => {
const background = document.getElementById("background");
const modal = document.getElementById("modal");
const openLink = document.getElementById("open-link");
openLink.click();
jest.runAllTimers()
expect(modal).toBeVisible()
background.click()
jest.runAllTimers()
expect(modal).not.toBeVisible()
});
});