@4site/engrid-scripts 0.21.10 → 0.22.0

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/a11y.d.ts CHANGED
@@ -3,4 +3,6 @@ export declare class A11y {
3
3
  private addGroupRole;
4
4
  private addRequired;
5
5
  private addLabel;
6
+ private updateFrequencyLabel;
7
+ private setAutoGeneratedAltTags;
6
8
  }
package/dist/a11y.js CHANGED
@@ -5,6 +5,9 @@ export class A11y {
5
5
  this.addRequired();
6
6
  this.addLabel();
7
7
  this.addGroupRole();
8
+ this.updateFrequencyLabel();
9
+ const ecardImages = document.querySelectorAll('.en__ecarditems__list img');
10
+ this.setAutoGeneratedAltTags(ecardImages);
8
11
  }
9
12
  addGroupRole() {
10
13
  // Add role="group" to all EN Radio fields
@@ -43,4 +46,51 @@ export class A11y {
43
46
  }
44
47
  });
45
48
  }
49
+ // Update the label for the frequency field based on the selected radio button
50
+ updateFrequencyLabel() {
51
+ const frequencyLabels = document.querySelectorAll('div.en__field__item input[id^="en__field_transaction_recurrfreq"]');
52
+ const frequencyMainLabel = document.querySelector('label[for="en__field_transaction_recurrfreq"]');
53
+ frequencyLabels.forEach((item) => {
54
+ if (item) {
55
+ // Set the label for the checked item on load
56
+ if (item.checked) {
57
+ frequencyMainLabel === null || frequencyMainLabel === void 0 ? void 0 : frequencyMainLabel.setAttribute('for', item.id);
58
+ }
59
+ // Then, detect if it changes with the click event
60
+ item.addEventListener('click', () => {
61
+ let frequencyId = item.id;
62
+ frequencyMainLabel === null || frequencyMainLabel === void 0 ? void 0 : frequencyMainLabel.setAttribute('for', frequencyId);
63
+ });
64
+ }
65
+ });
66
+ }
67
+ setAutoGeneratedAltTags(images) {
68
+ images.forEach((img) => {
69
+ var _a;
70
+ // Skip if the alt tag is already set
71
+ if (img.alt)
72
+ return;
73
+ try {
74
+ // Extract the filename from the `src` attribute
75
+ const src = img.src;
76
+ if (!src)
77
+ throw new Error("Image src is null or undefined");
78
+ const url = new URL(src);
79
+ const fileNameWithExtension = url.pathname.split('/').pop();
80
+ if (!fileNameWithExtension)
81
+ throw new Error("No filename found in src");
82
+ // Remove the file extension and replace `-` and `_` with spaces
83
+ let altText = ((_a = fileNameWithExtension.split('.').shift()) === null || _a === void 0 ? void 0 : _a.replace(/[-_]/g, ' ')) || '';
84
+ // Remove dimensions (#x#) and anything that follows
85
+ altText = altText.replace(/\d+x\d+.*$/, '').trim();
86
+ // Wrap in the disclaimer
87
+ altText = `This is an auto-generated alt tag from the filename: ${altText}`;
88
+ // Set the generated alt text on the image
89
+ img.alt = altText;
90
+ }
91
+ catch (error) {
92
+ console.error(`Error processing image: ${img.src}`, error);
93
+ }
94
+ });
95
+ }
46
96
  }
@@ -23,8 +23,10 @@ export class EcardToTarget {
23
23
  shouldRun() {
24
24
  return (window.hasOwnProperty("EngridEcardToTarget") &&
25
25
  typeof window.EngridEcardToTarget === "object" &&
26
- window.EngridEcardToTarget.hasOwnProperty("targetName") &&
27
- window.EngridEcardToTarget.hasOwnProperty("targetEmail"));
26
+ ((window.EngridEcardToTarget.hasOwnProperty("targetName") &&
27
+ window.EngridEcardToTarget.hasOwnProperty("targetEmail")) ||
28
+ (window.EngridEcardToTarget.hasOwnProperty("targets") &&
29
+ window.EngridEcardToTarget.targets.length > 0)));
28
30
  }
29
31
  setTarget() {
30
32
  const targetNameField = document.querySelector(".en__ecardrecipients__name input");
@@ -34,10 +36,30 @@ export class EcardToTarget {
34
36
  this.logger.error("Could not add recipient. Required elements not found.");
35
37
  return;
36
38
  }
37
- targetNameField.value = this.options.targetName;
38
- targetEmailField.value = this.options.targetEmail;
39
- addRecipientButton === null || addRecipientButton === void 0 ? void 0 : addRecipientButton.click();
40
- this.logger.log("Added recipient", this.options.targetName, this.options.targetEmail);
39
+ let targets = this.options.targets;
40
+ // BC support for targetName and targetEmail
41
+ if (this.options.targetName && this.options.targetEmail) {
42
+ targets.push({
43
+ targetName: this.options.targetName,
44
+ targetEmail: this.options.targetEmail,
45
+ });
46
+ }
47
+ // Remove duplicates from targets array
48
+ targets = targets.filter((target, index, self) => index ===
49
+ self.findIndex((t) => t.targetName === target.targetName &&
50
+ t.targetEmail === target.targetEmail));
51
+ targets.forEach((target) => {
52
+ const targetName = target.targetName;
53
+ const targetEmail = target.targetEmail;
54
+ if (!targetName || !targetEmail) {
55
+ this.logger.error("Could not add recipient. Target name or email is empty.");
56
+ return;
57
+ }
58
+ targetNameField.value = targetName;
59
+ targetEmailField.value = targetEmail;
60
+ addRecipientButton === null || addRecipientButton === void 0 ? void 0 : addRecipientButton.click();
61
+ this.logger.log("Added recipient", targetName, targetEmail);
62
+ });
41
63
  }
42
64
  hideElements() {
43
65
  const messageBlock = document.querySelector(".en__ecardmessage");
@@ -87,6 +87,7 @@ export class EmbeddedEcard {
87
87
  iframe.setAttribute("width", "100%");
88
88
  iframe.setAttribute("scrolling", "no");
89
89
  iframe.setAttribute("frameborder", "0");
90
+ iframe.setAttribute("title", "Ecard iframe");
90
91
  iframe.classList.add("engrid-iframe", "engrid-iframe--embedded-ecard");
91
92
  iframe.style.display = "none";
92
93
  return iframe;
@@ -5,5 +5,9 @@ export interface EcardToTargetOptions {
5
5
  hideTarget: boolean;
6
6
  hideMessage: boolean;
7
7
  addSupporterNameToMessage: boolean;
8
+ targets: {
9
+ targetName: string;
10
+ targetEmail: string;
11
+ }[];
8
12
  }
9
13
  export declare const EcardToTargetOptionsDefaults: EcardToTargetOptions;
@@ -5,4 +5,5 @@ export const EcardToTargetOptionsDefaults = {
5
5
  hideTarget: true,
6
6
  hideMessage: true,
7
7
  addSupporterNameToMessage: false,
8
+ targets: [],
8
9
  };
@@ -40,6 +40,7 @@ export class OptInLadder {
40
40
  iframe.allow = "payment";
41
41
  iframe.classList.add("opt-in-ladder-iframe");
42
42
  iframe.classList.add("engrid-iframe");
43
+ iframe.setAttribute("title", "Optin Ladder iframe");
43
44
  // If the page already has an iFrame with the same class, we don't need to add another one
44
45
  const existingIframe = document.querySelector(".opt-in-ladder-iframe");
45
46
  if (existingIframe) {
@@ -50,6 +50,7 @@ export class PostDonationEmbed {
50
50
  iframe.setAttribute("allowfullscreen", "");
51
51
  iframe.setAttribute("allowpaymentrequest", "true");
52
52
  iframe.setAttribute("allow", "payment");
53
+ iframe.setAttribute("title", "Post Donation iframe");
53
54
  // Replace <engrid-post-donation> with the iframe
54
55
  postDonationTag.replaceWith(iframe);
55
56
  }
@@ -1,10 +1,14 @@
1
1
  export declare class PremiumGift {
2
2
  private logger;
3
3
  private enElements;
4
+ private _frequency;
5
+ private _amount;
4
6
  constructor();
5
7
  shoudRun(): boolean;
6
8
  addEventListeners(): void;
7
9
  checkPremiumGift(): void;
8
10
  searchElements(): void;
9
11
  setPremiumTitle(title: string): void;
12
+ altsAndArias(): void;
13
+ maxDonationAria(): void;
10
14
  }
@@ -3,16 +3,23 @@
3
3
  // 2 - Add a class to body to indicate if the "maximize my impact" is selected (data-engrid-premium-gift-maximize="true|false")
4
4
  // 3 - Check the premium gift when click on the title or description
5
5
  // 4 - Create new {$PREMIUMTITLE} merge tag that's replaced with the premium gift name
6
- import { ENGrid, EngridLogger } from ".";
6
+ // 5 - Add aria-label to the radio inputs and alt tags to the images
7
+ import { ENGrid, DonationFrequency, DonationAmount, EngridLogger } from ".";
7
8
  export class PremiumGift {
8
9
  constructor() {
9
10
  this.logger = new EngridLogger("PremiumGift", "#232323", "#f7b500", "🎁");
10
11
  this.enElements = new Array();
12
+ this._frequency = DonationFrequency.getInstance();
13
+ this._amount = DonationAmount.getInstance();
11
14
  if (!this.shoudRun())
12
15
  return;
13
16
  this.searchElements();
14
17
  this.addEventListeners();
15
18
  this.checkPremiumGift();
19
+ window.setTimeout(() => {
20
+ this.altsAndArias();
21
+ this.maxDonationAria();
22
+ }, 1000);
16
23
  }
17
24
  shoudRun() {
18
25
  return ("pageJson" in window &&
@@ -61,6 +68,16 @@ export class PremiumGift {
61
68
  });
62
69
  observer.observe(premiumGiftsBlock, { attributes: true });
63
70
  }
71
+ this._frequency.onFrequencyChange.subscribe(() => {
72
+ window.setTimeout(() => {
73
+ this.altsAndArias();
74
+ }, 1000);
75
+ });
76
+ this._amount.onAmountChange.subscribe(() => {
77
+ window.setTimeout(() => {
78
+ this.altsAndArias();
79
+ }, 1000);
80
+ });
64
81
  }
65
82
  checkPremiumGift() {
66
83
  const premiumGift = document.querySelector('[name="en__pg"]:checked');
@@ -112,4 +129,55 @@ export class PremiumGift {
112
129
  }
113
130
  });
114
131
  }
132
+ // Sets alt tags for premium gift images and aria tags for premium gift radio inputs
133
+ altsAndArias() {
134
+ const premiumTitle = document.querySelectorAll(".en__pg__detail h2.en__pg__name");
135
+ const multistepBackButton = document.querySelectorAll(".multistep-button-container button.btn-back");
136
+ premiumTitle.forEach((item) => {
137
+ if (item) {
138
+ const titleText = item.innerHTML;
139
+ const parent = item.parentElement;
140
+ const prevSibling = parent === null || parent === void 0 ? void 0 : parent.previousElementSibling;
141
+ const radioInputSibling = prevSibling === null || prevSibling === void 0 ? void 0 : prevSibling.previousElementSibling;
142
+ if (prevSibling) {
143
+ const imageDiv = prevSibling.querySelector(".en__pg__images");
144
+ if (imageDiv) {
145
+ const img = imageDiv.querySelector("img");
146
+ if (img) {
147
+ img.setAttribute("alt", titleText);
148
+ img.style.width = "125px";
149
+ img.style.height = "100px";
150
+ }
151
+ }
152
+ }
153
+ if (radioInputSibling) {
154
+ const radioInput = radioInputSibling.querySelector('input[type="radio"]');
155
+ if (radioInput) {
156
+ radioInput.setAttribute("aria-label", titleText);
157
+ }
158
+ }
159
+ }
160
+ multistepBackButton.forEach((item) => {
161
+ item.setAttribute("aria-label", "Back");
162
+ });
163
+ });
164
+ }
165
+ // This is for the Maximize My Donation aria-label - the tree structure for it is slightly different.
166
+ maxDonationAria() {
167
+ const maxDonationTitle = Array.from(document.querySelectorAll(".en__pg__detail")).filter((el) => !el.querySelector("h2"));
168
+ maxDonationTitle.forEach((item) => {
169
+ var _a;
170
+ if (item) {
171
+ const titleText = ((_a = item.querySelector(".en__pg__description")) === null || _a === void 0 ? void 0 : _a.innerHTML) || "";
172
+ const prevSibling = item.previousElementSibling;
173
+ const radioInputSibling = prevSibling === null || prevSibling === void 0 ? void 0 : prevSibling.previousElementSibling;
174
+ if (radioInputSibling) {
175
+ const radioInput = radioInputSibling.querySelector('input[type="radio"]');
176
+ if (radioInput) {
177
+ radioInput.setAttribute("aria-label", titleText);
178
+ }
179
+ }
180
+ }
181
+ });
182
+ }
115
183
  }
@@ -226,6 +226,7 @@ export class RememberMe {
226
226
  "position:absolute;width:1px;height:1px;left:-9999px;";
227
227
  iframe.src = this.remoteUrl;
228
228
  iframe.setAttribute("sandbox", "allow-same-origin allow-scripts");
229
+ iframe.setAttribute("title", "Remember Me iframe");
229
230
  this.iframe = iframe;
230
231
  document.body.appendChild(this.iframe);
231
232
  this.iframe.addEventListener("load", () => iframeLoaded(), false);
@@ -45,6 +45,11 @@ export class ShowHideRadioCheckboxes {
45
45
  this.toggleValue(el, "hide");
46
46
  el.style.display = "none";
47
47
  this.logger.log("Hiding", el);
48
+ const input = el.querySelector("input");
49
+ if (input instanceof HTMLInputElement) {
50
+ input.setAttribute("aria-required", "false");
51
+ this.logger.log("aria-required set to FALSE", input);
52
+ }
48
53
  }
49
54
  });
50
55
  }
@@ -57,6 +62,11 @@ export class ShowHideRadioCheckboxes {
57
62
  this.toggleValue(el, "show");
58
63
  el.style.display = "";
59
64
  this.logger.log("Showing", el);
65
+ const input = el.querySelector("input");
66
+ if (input instanceof HTMLInputElement) {
67
+ input.setAttribute("aria-required", "true");
68
+ this.logger.log("aria-required set to TRUE", input);
69
+ }
60
70
  }
61
71
  });
62
72
  if (item.type == "checkbox" && !item.checked) {
package/dist/skip-link.js CHANGED
@@ -10,19 +10,19 @@ export class SkipToMainContentLink {
10
10
  const firstTitle = document.querySelector("title");
11
11
  const firstH1 = document.querySelector("h1");
12
12
  if (firstTitleInEngridBody && firstTitleInEngridBody.parentElement) {
13
- firstTitleInEngridBody.parentElement.insertAdjacentHTML("beforebegin", '<span id="skip-link"></span>');
13
+ firstTitleInEngridBody.parentElement.id = "skip-link";
14
14
  this.insertSkipLinkSpan();
15
15
  }
16
16
  else if (firstH1InEngridBody && firstH1InEngridBody.parentElement) {
17
- firstH1InEngridBody.parentElement.insertAdjacentHTML("beforebegin", '<span id="skip-link"></span>');
17
+ firstH1InEngridBody.parentElement.id = "skip-link";
18
18
  this.insertSkipLinkSpan();
19
19
  }
20
20
  else if (firstTitle && firstTitle.parentElement) {
21
- firstTitle.parentElement.insertAdjacentHTML("beforebegin", '<span id="skip-link"></span>');
21
+ firstTitle.parentElement.id = "skip-link";
22
22
  this.insertSkipLinkSpan();
23
23
  }
24
24
  else if (firstH1 && firstH1.parentElement) {
25
- firstH1.parentElement.insertAdjacentHTML("beforebegin", '<span id="skip-link"></span>');
25
+ firstH1.parentElement.id = "skip-link";
26
26
  this.insertSkipLinkSpan();
27
27
  }
28
28
  else {
@@ -57,6 +57,7 @@ export class SwapAmounts {
57
57
  const urlParams = new URLSearchParams(window.location.search);
58
58
  const amounts = urlParams.get("engrid-amounts");
59
59
  if (amounts) {
60
+ this.defaultChange = true;
60
61
  const amountArray = amounts.split(",").map((amt) => amt.trim());
61
62
  const defaultAmount = parseFloat(ENGrid.getUrlParameter("transaction.donationAmt")) || parseFloat(amountArray[0]);
62
63
  const amountsObj = {};
@@ -78,7 +79,9 @@ export class SwapAmounts {
78
79
  ignoreCurrentValue: this.ignoreCurrentValue(),
79
80
  });
80
81
  this._amount.load();
81
- this.logger.log("Amounts Swapped To", window.EngridAmounts[this._frequency.frequency]);
82
+ this.logger.log("Amounts Swapped To", window.EngridAmounts[this._frequency.frequency], {
83
+ ignoreCurrentValue: this.ignoreCurrentValue(),
84
+ });
82
85
  this.swapped = true;
83
86
  }
84
87
  }
@@ -97,8 +100,13 @@ export class SwapAmounts {
97
100
  return "EngridAmounts" in window;
98
101
  }
99
102
  ignoreCurrentValue() {
103
+ if (ENGrid.getUrlParameter("transaction.donationAmt") !== null) {
104
+ return this._amount.amount ===
105
+ parseFloat(ENGrid.getUrlParameter("transaction.donationAmt"))
106
+ ? false
107
+ : true;
108
+ }
100
109
  return !(window.EngagingNetworks.require._defined.enjs.checkSubmissionFailed() ||
101
- ENGrid.getUrlParameter("transaction.donationAmt") !== null ||
102
110
  this.defaultChange);
103
111
  }
104
112
  }
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const AppVersion = "0.21.10";
1
+ export declare const AppVersion = "0.22.0";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const AppVersion = "0.21.10";
1
+ export const AppVersion = "0.22.0";
@@ -1,4 +1,5 @@
1
1
  export declare class WelcomeBack {
2
+ private _form;
2
3
  private supporterDetails;
3
4
  private options;
4
5
  private rememberMeEvents;
@@ -10,4 +11,5 @@ export declare class WelcomeBack {
10
11
  private resetWelcomeBack;
11
12
  private addPersonalDetailsSummary;
12
13
  private addEventListeners;
14
+ enOnValidate(): void;
13
15
  }
@@ -7,11 +7,12 @@
7
7
  *
8
8
  * All the text content and positioning is configurable through the "WelcomeBack" option.
9
9
  */
10
- import { ENGrid, RememberMeEvents } from ".";
10
+ import { ENGrid, EnForm, RememberMeEvents } from ".";
11
11
  import * as cookie from "./cookie";
12
12
  export class WelcomeBack {
13
13
  constructor() {
14
14
  var _a;
15
+ this._form = EnForm.getInstance();
15
16
  this.supporterDetails = {};
16
17
  this.options = (_a = ENGrid.getOption("WelcomeBack")) !== null && _a !== void 0 ? _a : false;
17
18
  this.rememberMeEvents = RememberMeEvents.getInstance();
@@ -127,5 +128,17 @@ export class WelcomeBack {
127
128
  this.resetWelcomeBack();
128
129
  });
129
130
  });
131
+ this._form.onValidate.subscribe(this.enOnValidate.bind(this));
132
+ }
133
+ enOnValidate() {
134
+ if (!this._form.validate)
135
+ return;
136
+ const regionFieldValue = ENGrid.getFieldValue("supporter.region");
137
+ const regionFieldLabel = document.querySelector(".en__field--region label");
138
+ if (regionFieldLabel && regionFieldValue === "") {
139
+ ENGrid.setError(".en__field--region", `${regionFieldLabel.innerText} is required`);
140
+ ENGrid.setBodyData("hide-fast-personal-details", false);
141
+ this._form.validate = false;
142
+ }
130
143
  }
131
144
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@4site/engrid-scripts",
3
- "version": "0.21.10",
3
+ "version": "0.22.0",
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": "48bf3dcfd3908d0b99f333fed182d4c362c45b72",
28
+ "gitHead": "e5d7823be14fd1c1116bd43d80be85da4c1e69fd",
29
29
  "devDependencies": {
30
30
  "nodemon": "^2.0.20"
31
31
  }