@4site/engrid-scripts 0.25.6 → 0.25.8

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.
@@ -10,5 +10,7 @@ export declare class PremiumGift {
10
10
  searchElements(): void;
11
11
  setPremiumTitle(title: string): void;
12
12
  altsAndArias(): void;
13
+ syncOptionSelectStates(): void;
14
+ altsAndAriasForSelects(optionTypesParent: Element, titleText: string, premiumGiftId: string): void;
13
15
  maxDonationAria(): void;
14
16
  }
@@ -40,11 +40,14 @@ export class PremiumGift {
40
40
  if (newPremiumGift) {
41
41
  newPremiumGift.checked = true;
42
42
  newPremiumGift.dispatchEvent(new Event("change"));
43
+ this.altsAndArias();
43
44
  }
44
45
  }, 100);
45
46
  }
46
47
  window.setTimeout(() => {
47
48
  this.checkPremiumGift();
49
+ this.altsAndArias();
50
+ this.maxDonationAria();
48
51
  }, 110);
49
52
  }
50
53
  });
@@ -131,37 +134,80 @@ export class PremiumGift {
131
134
  }
132
135
  // Sets alt tags for premium gift images and aria tags for premium gift radio inputs
133
136
  altsAndArias() {
134
- const premiumTitle = document.querySelectorAll(".en__pg__detail h2.en__pg__name");
137
+ const premiumHeader = document.querySelector(".en__pgHeader");
138
+ const radioGroup = document.querySelector(".en__pgList");
139
+ if (premiumHeader && radioGroup) {
140
+ const premiumHeaderId = premiumHeader.id || "premium-gift-header";
141
+ premiumHeader.setAttribute("id", premiumHeaderId);
142
+ premiumHeader.setAttribute("role", "heading");
143
+ premiumHeader.setAttribute("aria-level", "2");
144
+ radioGroup.setAttribute("aria-labelledby", premiumHeaderId);
145
+ radioGroup.setAttribute("role", "radiogroup");
146
+ }
135
147
  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
- }
148
+ multistepBackButton.forEach((item) => {
149
+ item.setAttribute("aria-label", "Back");
150
+ });
151
+ const premiumRow = document.querySelectorAll(".en__pg");
152
+ premiumRow.forEach((item) => {
153
+ const premiumTitle = item.querySelector(".en__pg__detail h2.en__pg__name");
154
+ const titleText = (premiumTitle === null || premiumTitle === void 0 ? void 0 : premiumTitle.innerHTML) || "";
155
+ const premiumGiftInput = item.querySelector('input[name="en__pg"]');
156
+ const premiumGiftId = (premiumGiftInput === null || premiumGiftInput === void 0 ? void 0 : premiumGiftInput.value) || ENGrid.slugify(titleText);
157
+ premiumTitle === null || premiumTitle === void 0 ? void 0 : premiumTitle.setAttribute("id", `premium-gift-option-${premiumGiftId}`);
158
+ const details = item.querySelector(".en__pg__detail");
159
+ const display = item.querySelector(".en__pg__display");
160
+ const select = item.querySelector(".en__pg__select");
161
+ if (select) {
162
+ const radioInput = select.querySelector('input[type="radio"]');
163
+ if (radioInput) {
164
+ radioInput.setAttribute("aria-labelledby", (premiumTitle === null || premiumTitle === void 0 ? void 0 : premiumTitle.id) || "");
152
165
  }
153
- if (radioInputSibling) {
154
- const radioInput = radioInputSibling.querySelector('input[type="radio"]');
155
- if (radioInput) {
156
- radioInput.setAttribute("aria-label", titleText);
166
+ }
167
+ if (details) {
168
+ const optionTypesParent = details.querySelector(".en__pg__optionTypes");
169
+ if (optionTypesParent) {
170
+ this.altsAndAriasForSelects(optionTypesParent, titleText, premiumGiftId);
171
+ }
172
+ }
173
+ if (display) {
174
+ const imageDiv = display.querySelector(".en__pg__images");
175
+ if (imageDiv) {
176
+ const img = imageDiv.querySelector("img");
177
+ if (img) {
178
+ img.setAttribute("alt", titleText);
179
+ img.style.width = "125px";
180
+ img.style.height = "100px";
157
181
  }
158
182
  }
159
183
  }
160
- multistepBackButton.forEach((item) => {
161
- item.setAttribute("aria-label", "Back");
184
+ });
185
+ this.syncOptionSelectStates();
186
+ }
187
+ syncOptionSelectStates() {
188
+ const premiumRows = document.querySelectorAll(".en__pg");
189
+ premiumRows.forEach((row) => {
190
+ const radioInput = row.querySelector('input[name="en__pg"]');
191
+ const optionSelects = row.querySelectorAll(".en__pg__optionType select");
192
+ optionSelects.forEach((select) => {
193
+ select.disabled = !(radioInput === null || radioInput === void 0 ? void 0 : radioInput.checked);
162
194
  });
163
195
  });
164
196
  }
197
+ altsAndAriasForSelects(optionTypesParent, titleText, premiumGiftId) {
198
+ optionTypesParent.setAttribute("aria-label", `Options for ${titleText}`);
199
+ const optionTypes = optionTypesParent.querySelectorAll(".en__pg__optionType");
200
+ optionTypes.forEach((option, index) => {
201
+ const label = option.querySelector("label");
202
+ const select = option.querySelector('select');
203
+ if (label && select) {
204
+ const labelId = ENGrid.slugify(label.innerText) || index.toString();
205
+ select.setAttribute("id", `premium-gift-option-type-${premiumGiftId}-${labelId}`);
206
+ label.setAttribute("for", select.id);
207
+ label.setAttribute("aria-label", `${label.innerText} for ${titleText}`);
208
+ }
209
+ });
210
+ }
165
211
  // This is for the Maximize My Donation aria-label - the tree structure for it is slightly different.
166
212
  maxDonationAria() {
167
213
  const maxDonationTitle = Array.from(document.querySelectorAll(".en__pg__detail")).filter((el) => !el.querySelector("h2"));
@@ -83,11 +83,9 @@ export class RememberMe {
83
83
  let hasFieldData = Object.keys(this.fieldData).length > 0;
84
84
  if (!hasFieldData) {
85
85
  this.insertRememberMeOptin();
86
- this.rememberMeOptIn = false;
87
86
  }
88
87
  else {
89
88
  this.insertClearRememberMeLink();
90
- this.rememberMeOptIn = true;
91
89
  }
92
90
  this.writeFields();
93
91
  this._form.onSubmit.subscribe(() => {
@@ -4,7 +4,10 @@ export declare class SupporterHub {
4
4
  constructor();
5
5
  shoudRun(): boolean;
6
6
  watch(): void;
7
+ pageAltsAndArias(): void;
7
8
  creditCardUpdate(overlay: HTMLDivElement): void;
8
9
  amountLabelUpdate(overlay: HTMLDivElement): void;
10
+ dialogAltsAndArias(overlay: HTMLDivElement): void;
11
+ inertPage(inert: boolean, overlay?: HTMLDivElement): void;
9
12
  private preventDuplicateSubmits;
10
13
  }
@@ -9,6 +9,7 @@ export class SupporterHub {
9
9
  this.logger.log("Enabled");
10
10
  this.watch();
11
11
  this.preventDuplicateSubmits();
12
+ this.pageAltsAndArias();
12
13
  }
13
14
  shoudRun() {
14
15
  return ("pageJson" in window &&
@@ -29,6 +30,17 @@ export class SupporterHub {
29
30
  this.logger.log("Overlay found");
30
31
  this.creditCardUpdate(node);
31
32
  this.amountLabelUpdate(node);
33
+ this.dialogAltsAndArias(node);
34
+ }
35
+ }
36
+ });
37
+ mutation.removedNodes.forEach((node) => {
38
+ if (node.nodeName === "DIV") {
39
+ const overlay = node;
40
+ if (overlay.classList.contains("en__hubOverlay") ||
41
+ overlay.classList.contains("en__hubPledge__panels")) {
42
+ this.logger.log("Overlay removed");
43
+ this.inertPage(false);
32
44
  }
33
45
  }
34
46
  });
@@ -45,8 +57,36 @@ export class SupporterHub {
45
57
  if (hubOverlay) {
46
58
  this.creditCardUpdate(hubOverlay);
47
59
  this.amountLabelUpdate(hubOverlay);
60
+ this.dialogAltsAndArias(hubOverlay);
48
61
  }
49
62
  }
63
+ pageAltsAndArias() {
64
+ // Find every en__component--hubgadget and set role as button and aria-label as the span content of the component
65
+ document.querySelectorAll(".en__component--hubgadget").forEach((node) => {
66
+ const button = node;
67
+ const labelSpan = button.querySelector("span");
68
+ if (!labelSpan)
69
+ return;
70
+ const img = button.querySelector("img");
71
+ img === null || img === void 0 ? void 0 : img.setAttribute("aria-hidden", "true");
72
+ const slug = ENGrid.slugify(labelSpan.innerText);
73
+ const labelId = `hubgadget-label-${slug}`;
74
+ labelSpan.setAttribute("id", labelId);
75
+ button.setAttribute("aria-labelledby", labelId);
76
+ button.setAttribute("role", "button");
77
+ button.setAttribute("aria-controls", `huboverlay-${slug}`);
78
+ button.setAttribute("aria-haspopup", "dialog");
79
+ if (!button.classList.contains("en__component--hubgadget--inactive")) {
80
+ button.setAttribute("tabindex", "0");
81
+ button.addEventListener("keydown", (e) => {
82
+ if (e.key === "Enter" || e.key === " ") {
83
+ e.preventDefault();
84
+ button.click();
85
+ }
86
+ });
87
+ }
88
+ });
89
+ }
50
90
  creditCardUpdate(overlay) {
51
91
  window.setTimeout(() => {
52
92
  // Check if the overlay has Credit Card field and Update Button
@@ -73,6 +113,70 @@ export class SupporterHub {
73
113
  }
74
114
  }, 300);
75
115
  }
116
+ dialogAltsAndArias(overlay) {
117
+ window.setTimeout(() => {
118
+ this.inertPage(true, overlay);
119
+ const header = overlay.querySelector(".en__hubOverlay__header"), closeButton = header.querySelector("a");
120
+ // Tag close button
121
+ if (header && closeButton) {
122
+ closeButton.setAttribute("role", "button");
123
+ closeButton.setAttribute("aria-label", "Close");
124
+ }
125
+ // Tag header and label dialog
126
+ const headerTitle = header.querySelector("h2");
127
+ const slug = ENGrid.slugify((headerTitle === null || headerTitle === void 0 ? void 0 : headerTitle.innerText) || "supporter-hub-overlay");
128
+ let headerTitleId = `huboverlay-title-${slug}`;
129
+ if (headerTitle) {
130
+ headerTitleId = headerTitle.id || headerTitleId;
131
+ headerTitle.setAttribute("id", headerTitleId);
132
+ }
133
+ const popup = overlay.querySelector(".en__hubOverlay__popup");
134
+ if (popup) {
135
+ popup.setAttribute("id", `huboverlay-${slug}`);
136
+ popup.setAttribute("role", "dialog");
137
+ popup.setAttribute("aria-modal", "true");
138
+ if (headerTitle) {
139
+ popup.setAttribute("aria-labelledby", headerTitleId);
140
+ }
141
+ else {
142
+ popup.setAttribute("aria-label", "Supporter Hub Overlay");
143
+ }
144
+ }
145
+ }, 300);
146
+ }
147
+ inertPage(inert, overlay) {
148
+ if (inert) {
149
+ const hubOverlay = overlay && overlay.classList.contains("en__hubOverlay")
150
+ ? overlay
151
+ : document.querySelector(".en__hubOverlay") ||
152
+ overlay;
153
+ if (!hubOverlay)
154
+ return;
155
+ let element = hubOverlay;
156
+ while (element && element !== document.body) {
157
+ const parent = element.parentElement;
158
+ if (parent) {
159
+ Array.from(parent.children).forEach((sibling) => {
160
+ if (sibling !== element &&
161
+ sibling instanceof HTMLElement &&
162
+ !sibling.hasAttribute("inert")) {
163
+ sibling.setAttribute("inert", "");
164
+ sibling.dataset.engridInert = "true";
165
+ }
166
+ });
167
+ }
168
+ element = parent;
169
+ }
170
+ }
171
+ else if (!document.querySelector(".en__hubOverlay, .en__hubPledge__panels")) {
172
+ document
173
+ .querySelectorAll("[data-engrid-inert]")
174
+ .forEach((element) => {
175
+ element.removeAttribute("inert");
176
+ delete element.dataset.engridInert;
177
+ });
178
+ }
179
+ }
76
180
  // The supporter hub does not properly handle or prevent duplicate submits, so we add a listener to prevent this.
77
181
  preventDuplicateSubmits() {
78
182
  document.addEventListener("click", (e) => {
@@ -32,13 +32,20 @@ export declare class SwapAmounts {
32
32
  private _frequency;
33
33
  private defaultChange;
34
34
  private swapped;
35
+ private hasOneTimeNSG;
36
+ private hasRecurringNSG;
35
37
  constructor();
36
38
  loadAmountsFromUrl(): void;
37
39
  swapAmounts(): void;
40
+ private shouldUseNSG;
38
41
  /**
39
42
  * Convert the internal config object into the structure Engaging Networks expects
40
43
  */
41
44
  private toEnAmountList;
45
+ /**
46
+ * Convert the Engaging Networks NSG config object into the structure Engaging Network Lists expect
47
+ */
48
+ private toEnAmountListNSG;
42
49
  shouldRun(): boolean;
43
50
  ignoreCurrentValue(): boolean;
44
51
  }
@@ -38,7 +38,18 @@ export class SwapAmounts {
38
38
  this._frequency = DonationFrequency.getInstance();
39
39
  this.defaultChange = false; // Tracks if user changed away from default after swap
40
40
  this.swapped = false; // Tracks if we've already executed at least one swap
41
+ this.hasOneTimeNSG = false;
42
+ this.hasRecurringNSG = false;
41
43
  this.loadAmountsFromUrl();
44
+ this.hasOneTimeNSG = !!(window.EngagingNetworks.suggestedGift &&
45
+ window.EngagingNetworks.suggestedGift.single &&
46
+ window.EngagingNetworks.suggestedGift.single.length > 0);
47
+ this.hasRecurringNSG = !!(window.EngagingNetworks.suggestedGift &&
48
+ window.EngagingNetworks.suggestedGift.recurring &&
49
+ window.EngagingNetworks.suggestedGift.recurring.length > 0);
50
+ if (this.hasOneTimeNSG || this.hasRecurringNSG) {
51
+ this.logger.log("Detected NSG amounts", { suggestedGift: window.EngagingNetworks.suggestedGift });
52
+ }
42
53
  if (!this.shouldRun())
43
54
  return;
44
55
  // Respond when frequency changes
@@ -98,6 +109,13 @@ export class SwapAmounts {
98
109
  const config = configs[freq];
99
110
  if (!config)
100
111
  return;
112
+ if (this.shouldUseNSG(freq, config)) {
113
+ this.logger.log(`NSG present for ${freq}, using NSG amounts`, { suggestedGift: window.EngagingNetworks.suggestedGift });
114
+ window.EngagingNetworks.require._defined.enjs.swapList("donationAmt", this.toEnAmountListNSG(window.EngagingNetworks.suggestedGift, freq), { ignoreCurrentValue: true });
115
+ this._amount.load();
116
+ this.swapped = true;
117
+ return;
118
+ }
101
119
  const stickyDefault = !!config.stickyDefault;
102
120
  // If stickyDefault, always ignore current value so selected flag in list enforces default
103
121
  const ignoreCurrentValue = stickyDefault ? true : this.ignoreCurrentValue();
@@ -106,6 +124,15 @@ export class SwapAmounts {
106
124
  this.logger.log("Amounts Swapped To", config, { ignoreCurrentValue });
107
125
  this.swapped = true;
108
126
  }
127
+ shouldUseNSG(freq, config) {
128
+ if (freq === "onetime" && this.hasOneTimeNSG && !config.overrideNSG) {
129
+ return true;
130
+ }
131
+ if (freq === "monthly" && this.hasRecurringNSG && !config.overrideNSG) {
132
+ return true;
133
+ }
134
+ return false;
135
+ }
109
136
  /**
110
137
  * Convert the internal config object into the structure Engaging Networks expects
111
138
  */
@@ -116,13 +143,19 @@ export class SwapAmounts {
116
143
  value: value.toString(),
117
144
  }));
118
145
  }
146
+ /**
147
+ * Convert the Engaging Networks NSG config object into the structure Engaging Network Lists expect
148
+ */
149
+ toEnAmountListNSG(config, freq) {
150
+ const frequency = freq === "onetime" ? "single" : "recurring";
151
+ return config[frequency].map(({ nextSuggestedGift, value }) => ({
152
+ selected: nextSuggestedGift,
153
+ label: value > 0 ? value.toString() : "Other",
154
+ value: value > 0 ? value.toString() : "other",
155
+ }));
156
+ }
119
157
  shouldRun() {
120
- const hasNSG = window.EngagingNetworks.suggestedGift !== undefined &&
121
- Object.keys(window.EngagingNetworks.suggestedGift).length > 0;
122
- if (!!window.EngridAmounts && hasNSG) {
123
- this.logger.log("Not swapping amounts because NSG is active on page");
124
- }
125
- return !!window.EngridAmounts && !hasNSG;
158
+ return !!window.EngridAmounts;
126
159
  }
127
160
  ignoreCurrentValue() {
128
161
  const urlParam = ENGrid.getUrlParameter("transaction.donationAmt");
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const AppVersion = "0.25.6";
1
+ export declare const AppVersion = "0.25.8";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const AppVersion = "0.25.6";
1
+ export const AppVersion = "0.25.8";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@4site/engrid-scripts",
3
- "version": "0.25.6",
3
+ "version": "0.25.8",
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": "e538e7e8e084d63b785781e581159f3dee15b8b4",
28
+ "gitHead": "e03f79235e88093268971e3d7bd637877f9199bd",
29
29
  "devDependencies": {
30
30
  "nodemon": "^2.0.20"
31
31
  }