@4site/engrid-scripts 0.22.3 → 0.22.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/app.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { DonationAmount, DonationFrequency, EnForm, ProcessingFees, Country, } from "./events";
2
- import { AmountLabel, Loader, ProgressBar, UpsellLightbox, ENGrid, OptionsDefaults, setRecurrFreq, PageBackground, MediaAttribution, ApplePay, A11y, CapitalizeFields, Ecard, ClickToExpand, Advocacy, DataAttributes, LiveVariables, iFrame, InputPlaceholders, InputHasValueAndFocus, ShowHideRadioCheckboxes, AutoCountrySelect, SkipToMainContentLink, SrcDefer, NeverBounce, AutoYear, Autocomplete, RememberMe, TranslateFields, ShowIfAmount, EngridLogger, OtherAmount, MinMaxAmount, Ticker, DataReplace, DataHide, AddNameToMessage, ExpandRegionName, AppVersion, UrlToForm, RequiredIfVisible, TidyContact, DataLayer, LiveCurrency, Autosubmit, EventTickets, SwapAmounts, DebugPanel, DebugHiddenFields, FreshAddress, BrandingHtml, CountryDisable, PremiumGift, DigitalWallets, MobileCTA, LiveFrequency, UniversalOptIn, Plaid, GiveBySelect, UrlParamsToBodyAttrs, ExitIntentLightbox, SupporterHub, FastFormFill, SetAttr, ShowIfPresent, ENValidators, CustomCurrency, VGS, PostalCodeValidator, CountryRedirect, WelcomeBack, EcardToTarget, UsOnlyForm, ThankYouPageConditionalContent, EmbeddedEcard, CheckboxLabel, UpsellCheckbox, PostDonationEmbed, } from ".";
2
+ import { AmountLabel, Loader, ProgressBar, UpsellLightbox, ENGrid, OptionsDefaults, setRecurrFreq, PageBackground, MediaAttribution, ApplePay, A11y, CapitalizeFields, Ecard, ClickToExpand, Advocacy, DataAttributes, LiveVariables, iFrame, InputPlaceholders, InputHasValueAndFocus, ShowHideRadioCheckboxes, AutoCountrySelect, SkipToMainContentLink, SrcDefer, NeverBounce, AutoYear, Autocomplete, RememberMe, TranslateFields, ShowIfAmount, EngridLogger, OtherAmount, MinMaxAmount, Ticker, DataReplace, DataHide, AddNameToMessage, ExpandRegionName, AppVersion, UrlToForm, RequiredIfVisible, TidyContact, DataLayer, LiveCurrency, Autosubmit, EventTickets, SwapAmounts, DebugPanel, DebugHiddenFields, FreshAddress, BrandingHtml, CountryDisable, PremiumGift, DigitalWallets, MobileCTA, LiveFrequency, UniversalOptIn, Plaid, GiveBySelect, UrlParamsToBodyAttrs, ExitIntentLightbox, SupporterHub, FastFormFill, SetAttr, ShowIfPresent, ENValidators, CustomCurrency, VGS, PostalCodeValidator, CountryRedirect, WelcomeBack, EcardToTarget, UsOnlyForm, ThankYouPageConditionalContent, EmbeddedEcard, CheckboxLabel, UpsellCheckbox, PostDonationEmbed, FrequencyUpsell, } from ".";
3
3
  export class App extends ENGrid {
4
4
  constructor(options) {
5
5
  super();
@@ -276,6 +276,7 @@ export class App extends ENGrid {
276
276
  new EmbeddedEcard();
277
277
  new CheckboxLabel();
278
278
  new PostDonationEmbed();
279
+ new FrequencyUpsell();
279
280
  //Debug panel
280
281
  let showDebugPanel = this.options.Debug;
281
282
  try {
package/dist/engrid.js CHANGED
@@ -334,10 +334,12 @@ export class ENGrid {
334
334
  const submit = document.querySelector(".en__submit button");
335
335
  if (!submit)
336
336
  return false;
337
+ let submitButtonProcessingHTML = `<span class='loader-wrapper'><span class='loader loader-quart'></span><span class='submit-button-text-wrapper'>${label}</span></span>`;
338
+ if ("originalText" in submit.dataset && submit.disabled) {
339
+ // If the original text is already set and the button is disabled, that means this function was called before
340
+ return false;
341
+ }
337
342
  submit.dataset.originalText = submit.innerHTML;
338
- let submitButtonProcessingHTML = "<span class='loader-wrapper'><span class='loader loader-quart'></span><span class='submit-button-text-wrapper'>" +
339
- label +
340
- "</span></span>";
341
343
  submit.disabled = true;
342
344
  submit.innerHTML = submitButtonProcessingHTML;
343
345
  return true;
@@ -0,0 +1,12 @@
1
+ import { FrequencyUpsellOptions, Modal } from ".";
2
+ export declare class FrequencyUpsellModal extends Modal {
3
+ private readonly upsellOptions;
4
+ private _amountWithFees;
5
+ private _upsellAmountWithFees;
6
+ constructor(upsellOptions: FrequencyUpsellOptions);
7
+ set amountWithFees(value: number);
8
+ set upsellAmountWithFees(value: number);
9
+ updateModalContent(): void;
10
+ getModalContent(): NodeListOf<Element> | HTMLElement | string;
11
+ replaceAmountTokens(string: string): string;
12
+ }
@@ -0,0 +1,62 @@
1
+ /*
2
+ * FrequencyUpsellModal - this is class that creates the modal for the frequency upsell.
3
+ * This component is intentionally "dumb" and only creates the modal renders its content.
4
+ * Logic for showing the modal and handling the upsell is in the FrequencyUpsell class.
5
+ */
6
+ import { ENGrid, Modal } from ".";
7
+ export class FrequencyUpsellModal extends Modal {
8
+ constructor(upsellOptions) {
9
+ super({
10
+ onClickOutside: "bounce",
11
+ customClass: `engrid--frequency-upsell-modal ${upsellOptions.customClass}`,
12
+ showCloseX: false,
13
+ });
14
+ this._amountWithFees = 0;
15
+ this._upsellAmountWithFees = 0;
16
+ this.upsellOptions = upsellOptions;
17
+ this.updateModalContent();
18
+ }
19
+ set amountWithFees(value) {
20
+ this._amountWithFees = value;
21
+ }
22
+ set upsellAmountWithFees(value) {
23
+ this._upsellAmountWithFees = value;
24
+ }
25
+ updateModalContent() {
26
+ var _a;
27
+ this.modalContent = this.getModalContent();
28
+ const modalBody = (_a = this.modal) === null || _a === void 0 ? void 0 : _a.querySelector(".engrid-modal__body");
29
+ if (modalBody) {
30
+ modalBody.innerHTML = "";
31
+ modalBody.insertAdjacentHTML("beforeend", this.modalContent);
32
+ }
33
+ }
34
+ getModalContent() {
35
+ if (!this.upsellOptions)
36
+ return "";
37
+ return `
38
+ <div class="frequency-upsell-modal__secondary-content"></div>
39
+ <div class="frequency-upsell-modal__content">
40
+ <div class="frequency-upsell-modal__text">
41
+ <h2 class="frequency-upsell-modal__title">${this.replaceAmountTokens(this.upsellOptions.title)}</h2>
42
+ <p class="frequency-upsell-modal__para">${this.replaceAmountTokens(this.upsellOptions.paragraph)}</p>
43
+ </div>
44
+ <div class="frequency-upsell-modal__buttons">
45
+ <button class="primary frequency-upsell-modal__button" id="frequency-upsell-yes">
46
+ ${this.replaceAmountTokens(this.upsellOptions.yesButton)}
47
+ </button>
48
+ <button class="primary frequency-upsell-modal__button" id="frequency-upsell-no">
49
+ ${this.replaceAmountTokens(this.upsellOptions.noButton)}
50
+ </button>
51
+ </div>
52
+ </div>
53
+ `;
54
+ }
55
+ replaceAmountTokens(string) {
56
+ const amount = ENGrid.formatNumber(this._amountWithFees, this._amountWithFees % 1 == 0 ? 0 : 2, ".", "");
57
+ const upsellAmount = ENGrid.formatNumber(this._upsellAmountWithFees, this._upsellAmountWithFees % 1 == 0 ? 0 : 2, ".", "");
58
+ return string
59
+ .replace(/{current_amount}/g, amount)
60
+ .replace(/{upsell_amount}/g, upsellAmount);
61
+ }
62
+ }
@@ -0,0 +1,32 @@
1
+ export declare class FrequencyUpsell {
2
+ private logger;
3
+ private upsellModal;
4
+ private readonly options;
5
+ private _frequency;
6
+ private _amount;
7
+ private _fee;
8
+ private _form;
9
+ private modalSeen;
10
+ constructor();
11
+ /**
12
+ * Check if the FrequencyUpsell should run:
13
+ * - Check if the FrequencyUpsell is enabled in the window object
14
+ * - Check that we don't have an EngridUpsell active on this page
15
+ * - Check that we don't have an EngagingNetworks upsell active on this page
16
+ * @returns {boolean} - true if the FrequencyUpsell should run, false otherwise
17
+ */
18
+ shouldRun(): boolean;
19
+ /**
20
+ * Get the upsell amount with/without fees
21
+ * We want to display to the user the amount with fees, but we need to set the donation amount to the value without fees
22
+ * @param {boolean} withFee - true if we want to include the fees in the upsell amount
23
+ * @returns {number} - The upsell amount with fees
24
+ */
25
+ getUpsellAmount(withFee: boolean): number;
26
+ private addEventListeners;
27
+ /**
28
+ * Create the frequency field for the upsell, if it does not exist on the page already
29
+ * This is required by DonationFrequency to set the frequency
30
+ */
31
+ private createFrequencyField;
32
+ }
@@ -0,0 +1,116 @@
1
+ /*
2
+ * FrequencyUpsell component which creates a modal to upsell the frequency of the donation
3
+ * This is typically used to upsell a single donation into an annual donation, but the component
4
+ * options can be configured to upsell any frequency to any other frequency. The upsell amount can also be configured
5
+ * See FrequencyUpsellOptions for more details.
6
+ */
7
+ import { DonationAmount, DonationFrequency, EnForm, EngridLogger, FrequencyUpsellModal, FrequencyUpsellOptionsDefaults, ProcessingFees, } from ".";
8
+ export class FrequencyUpsell {
9
+ constructor() {
10
+ this.logger = new EngridLogger("FrequencyUpsell", "lightgray", "darkblue", "🏦");
11
+ this.upsellModal = null;
12
+ this.options = null;
13
+ this._frequency = DonationFrequency.getInstance();
14
+ this._amount = DonationAmount.getInstance();
15
+ this._fee = ProcessingFees.getInstance();
16
+ this._form = EnForm.getInstance();
17
+ this.modalSeen = false;
18
+ if (!this.shouldRun()) {
19
+ this.logger.log("FrequencyUpsell not running");
20
+ return;
21
+ }
22
+ this.options = Object.assign(Object.assign({}, FrequencyUpsellOptionsDefaults), window.EngridFrequencyUpsell);
23
+ this.logger.log("FrequencyUpsell initialized", this.options);
24
+ this.upsellModal = new FrequencyUpsellModal(this.options);
25
+ this.createFrequencyField();
26
+ this.addEventListeners();
27
+ }
28
+ /**
29
+ * Check if the FrequencyUpsell should run:
30
+ * - Check if the FrequencyUpsell is enabled in the window object
31
+ * - Check that we don't have an EngridUpsell active on this page
32
+ * - Check that we don't have an EngagingNetworks upsell active on this page
33
+ * @returns {boolean} - true if the FrequencyUpsell should run, false otherwise
34
+ */
35
+ shouldRun() {
36
+ return (window.EngridFrequencyUpsell &&
37
+ !window.EngridUpsell &&
38
+ (!window.EngagingNetworks.upsell ||
39
+ window.EngagingNetworks.upsell.length === 0));
40
+ }
41
+ /**
42
+ * Get the upsell amount with/without fees
43
+ * We want to display to the user the amount with fees, but we need to set the donation amount to the value without fees
44
+ * @param {boolean} withFee - true if we want to include the fees in the upsell amount
45
+ * @returns {number} - The upsell amount with fees
46
+ */
47
+ getUpsellAmount(withFee) {
48
+ if (withFee) {
49
+ const upsellAmount = this.options.upsellAmount(this._amount.amount);
50
+ return upsellAmount + this._fee.calculateFees(upsellAmount);
51
+ }
52
+ return this.options.upsellAmount(this._amount.amount);
53
+ }
54
+ addEventListeners() {
55
+ var _a, _b;
56
+ // When the Modal buttons are clicked
57
+ (_b = (_a = this.upsellModal) === null || _a === void 0 ? void 0 : _a.modal) === null || _b === void 0 ? void 0 : _b.addEventListener("click", (e) => {
58
+ const target = e.target;
59
+ // Upsell is accepted
60
+ if (target.id === "frequency-upsell-yes") {
61
+ this.logger.log("Frequency upsell accepted");
62
+ this._frequency.setFrequency(this.options.upsellFrequency);
63
+ this._amount.setAmount(this.getUpsellAmount(false));
64
+ this.options.onAccept();
65
+ this._form.submitForm();
66
+ this.upsellModal.close();
67
+ return;
68
+ }
69
+ // Upsell is declined
70
+ if (target.id === "frequency-upsell-no") {
71
+ this.logger.log("Frequency upsell declined");
72
+ this.options.onDecline();
73
+ this._form.submitForm();
74
+ this.upsellModal.close();
75
+ return;
76
+ }
77
+ });
78
+ // When the form is submitted
79
+ this._form.onSubmit.subscribe(() => {
80
+ var _a;
81
+ // If we have a frequency we want to upsell on & the modal isn't already open
82
+ // Since frequency in the event class doesn't have a specific type, I need to cast our options array to a general string array
83
+ if (this.options.upsellFromFrequency.includes(this._frequency.frequency) &&
84
+ !this.modalSeen) {
85
+ // Open the modal and prevent form submission
86
+ this.upsellModal.amountWithFees =
87
+ this._amount.amount + this._fee.calculateFees(this._amount.amount);
88
+ this.upsellModal.upsellAmountWithFees = this.getUpsellAmount(true);
89
+ this.upsellModal.updateModalContent();
90
+ this.logger.log("Frequency upsell modal opened");
91
+ (_a = this.upsellModal) === null || _a === void 0 ? void 0 : _a.open();
92
+ this.modalSeen = true;
93
+ this._form.submit = false;
94
+ return false;
95
+ }
96
+ // If not opening, continue with the form submission
97
+ this._form.submit = true;
98
+ return true;
99
+ });
100
+ }
101
+ /**
102
+ * Create the frequency field for the upsell, if it does not exist on the page already
103
+ * This is required by DonationFrequency to set the frequency
104
+ */
105
+ createFrequencyField() {
106
+ const frequencyField = document.querySelector(`input[name="transaction.recurrfreq"][value="${this.options.upsellFrequency.toUpperCase()}"]`);
107
+ if (frequencyField)
108
+ return;
109
+ const frequencyFieldContainer = document.querySelector(".en__field--recurrfreq .en__field__element");
110
+ frequencyFieldContainer === null || frequencyFieldContainer === void 0 ? void 0 : frequencyFieldContainer.insertAdjacentHTML("beforeend", `
111
+ <div class="en__field__item hide">
112
+ <input type="radio" name="transaction.recurrfreq" value="${this.options.upsellFrequency.toUpperCase()}" class="en__field__input en__field__input--radio">
113
+ </div>
114
+ `);
115
+ }
116
+ }
package/dist/index.d.ts CHANGED
@@ -3,6 +3,7 @@ export { Options, OptionsDefaults } from "./interfaces/options";
3
3
  export { UpsellOptions, UpsellOptionsDefaults, } from "./interfaces/upsell-options";
4
4
  export { TranslateOptions, TranslateOptionsDefaults, } from "./interfaces/translate-options";
5
5
  export { ExitIntentOptions, ExitIntentOptionsDefaults, } from "./interfaces/exit-intent-options";
6
+ export { FrequencyUpsellOptions, FrequencyUpsellOptionsDefaults, } from "./interfaces/frequency-upsell-options";
6
7
  export * from "./loader";
7
8
  export * from "./app";
8
9
  export * from "./amount-label";
@@ -82,5 +83,7 @@ export * from "./thank-you-page-conditional-content";
82
83
  export * from "./checkbox-label";
83
84
  export * from "./optin-ladder";
84
85
  export * from "./post-donation-embed";
86
+ export * from "./frequency-upsell-modal";
87
+ export * from "./frequency-upsell";
85
88
  export * from "./events";
86
89
  export * from "./version";
package/dist/index.js CHANGED
@@ -3,6 +3,7 @@ export { OptionsDefaults } from "./interfaces/options";
3
3
  export { UpsellOptionsDefaults, } from "./interfaces/upsell-options";
4
4
  export { TranslateOptionsDefaults, } from "./interfaces/translate-options";
5
5
  export { ExitIntentOptionsDefaults, } from "./interfaces/exit-intent-options";
6
+ export { FrequencyUpsellOptionsDefaults, } from "./interfaces/frequency-upsell-options";
6
7
  export * from "./loader";
7
8
  export * from "./app";
8
9
  export * from "./amount-label";
@@ -82,6 +83,8 @@ export * from "./thank-you-page-conditional-content";
82
83
  export * from "./checkbox-label";
83
84
  export * from "./optin-ladder";
84
85
  export * from "./post-donation-embed";
86
+ export * from "./frequency-upsell-modal";
87
+ export * from "./frequency-upsell";
85
88
  // Events
86
89
  export * from "./events";
87
90
  // Version
@@ -0,0 +1,13 @@
1
+ export interface FrequencyUpsellOptions {
2
+ title: string;
3
+ paragraph: string;
4
+ yesButton: string;
5
+ noButton: string;
6
+ upsellFrequency: "monthly" | "quarterly" | "semi_annual" | "annual";
7
+ upsellAmount: (currentAmount: number) => number;
8
+ upsellFromFrequency: Array<"onetime" | "monthly" | "quarterly" | "semi_annual" | "annual">;
9
+ customClass: string;
10
+ onAccept: () => void;
11
+ onDecline: () => void;
12
+ }
13
+ export declare const FrequencyUpsellOptionsDefaults: FrequencyUpsellOptions;
@@ -0,0 +1,12 @@
1
+ export const FrequencyUpsellOptionsDefaults = {
2
+ title: "Before we process your donation...",
3
+ paragraph: "Would you like to make it an annual gift?",
4
+ yesButton: "YES! Process my gift as an annual gift of ${upsell_amount}",
5
+ noButton: "NO! Process my gift as a one-time gift of ${current_amount}",
6
+ upsellFrequency: "annual",
7
+ upsellFromFrequency: ["onetime"],
8
+ customClass: "",
9
+ upsellAmount: (currentAmount) => currentAmount,
10
+ onAccept: () => { },
11
+ onDecline: () => { },
12
+ };
package/dist/modal.d.ts CHANGED
@@ -1,11 +1,13 @@
1
1
  interface ModalOptions {
2
- onClickOutside: "close" | "bounce";
3
- addCloseButton: boolean;
4
- closeButtonLabel: string;
2
+ onClickOutside?: "close" | "bounce";
3
+ addCloseButton?: boolean;
4
+ closeButtonLabel?: string;
5
+ customClass?: string;
6
+ showCloseX?: boolean;
5
7
  }
6
8
  export declare abstract class Modal {
7
9
  modalContent: NodeListOf<Element> | HTMLElement | String;
8
- private modal;
10
+ modal: HTMLDivElement | null;
9
11
  private defaultOptions;
10
12
  private options;
11
13
  protected constructor(options: ModalOptions);
package/dist/modal.js CHANGED
@@ -6,6 +6,8 @@ export class Modal {
6
6
  onClickOutside: "close",
7
7
  addCloseButton: false,
8
8
  closeButtonLabel: "Okay!",
9
+ customClass: "",
10
+ showCloseX: true,
9
11
  };
10
12
  this.focusTrapHandler = (e) => {
11
13
  const modalElement = this.modal;
@@ -39,6 +41,16 @@ export class Modal {
39
41
  var _a;
40
42
  this.modal = document.createElement("div");
41
43
  this.modal.classList.add("engrid-modal", "modal--hidden");
44
+ if (this.options.customClass && this.options.customClass !== "") {
45
+ this.options.customClass.split(" ").forEach((customClass) => {
46
+ if (!customClass)
47
+ return;
48
+ this.modal.classList.add(customClass);
49
+ });
50
+ }
51
+ if (this.options.showCloseX) {
52
+ this.modal.classList.add("engrid-modal--close-x");
53
+ }
42
54
  this.modal.setAttribute("aria-hidden", "true");
43
55
  this.modal.setAttribute("role", "dialog");
44
56
  this.modal.setAttribute("aria-modal", "true");
@@ -46,7 +58,7 @@ export class Modal {
46
58
  this.modal.innerHTML = `
47
59
  <div class="engrid-modal__overlay" tabindex="-1">
48
60
  <div class="engrid-modal__container" tabindex="0">
49
- <div class="engrid-modal__close" role="button" tabindex="0" aria-label="Close">
61
+ <div class="engrid-modal__close engrid-modal__close-x" role="button" tabindex="0" aria-label="Close">
50
62
  X
51
63
  </div>
52
64
  <div class="engrid-modal__body"></div>
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const AppVersion = "0.22.3";
1
+ export declare const AppVersion = "0.22.4";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const AppVersion = "0.22.3";
1
+ export const AppVersion = "0.22.4";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@4site/engrid-scripts",
3
- "version": "0.22.3",
3
+ "version": "0.22.4",
4
4
  "description": "Scripts for the ENGRID Project",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -25,7 +25,7 @@
25
25
  "strongly-typed-events": "^2.0.9"
26
26
  },
27
27
  "homepage": "https://github.com/4site-interactive-studios/engrid#readme",
28
- "gitHead": "46a09414790ab639952e5f02c63dd8438629d078",
28
+ "gitHead": "2a0b3d4748a6caf4d742650b408419e21d095723",
29
29
  "devDependencies": {
30
30
  "nodemon": "^2.0.20"
31
31
  }