@4site/engrid-scripts 0.25.1 → 0.25.5

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/iframe.js CHANGED
@@ -137,6 +137,11 @@ export class iFrame {
137
137
  window.parent.postMessage({
138
138
  scroll: this.shouldScroll(),
139
139
  }, "*");
140
+ // Iframe Queue: signal Thank-You-page completion to the parent window.
141
+ // The IframeQueue component (in parent mode) listens for this ping and
142
+ // matches it by Page ID to advance to the next queued iframe. Fires
143
+ // exactly once per Thank-You-page load. See iframe-queue.ts.
144
+ this.sendIframeQueueThankYouPing();
140
145
  // On click fire the resize event
141
146
  document.addEventListener("click", (e) => {
142
147
  this.logger.log("iFrame Event - click");
@@ -147,6 +152,35 @@ export class iFrame {
147
152
  // Watch for errors and send the height
148
153
  ENGrid.watchForError(this.sendIframeHeight.bind(this));
149
154
  }
155
+ /**
156
+ * Posts a `engrid-iframe-queue:thank-you` message to the parent window
157
+ * when the embedded EN page reaches its Thank You page (the last page
158
+ * in the page sequence). Carries the Page ID of the submitting form so
159
+ * the IframeQueue parent can match the ping against the queued item it
160
+ * is waiting on, ignoring pings from unrelated EN iframes that may exist
161
+ * on the same parent page (e.g. an Embedded Ecard iframe).
162
+ *
163
+ * Only fires when:
164
+ * - the script is running inside an iframe (already guaranteed by the
165
+ * code path that calls onLoaded()), AND
166
+ * - the embedded page is a Thank You page (ENGrid.isThankYouPage()).
167
+ *
168
+ * Consumed by: IframeQueue (engrid/packages/scripts/src/iframe-queue.ts).
169
+ */
170
+ sendIframeQueueThankYouPing() {
171
+ if (!ENGrid.isThankYouPage())
172
+ return;
173
+ const pageId = ENGrid.getPageID();
174
+ const message = {
175
+ type: "engrid-iframe-queue:thank-you",
176
+ pageId,
177
+ pageNumber: ENGrid.getPageNumber(),
178
+ pageCount: ENGrid.getPageCount(),
179
+ url: window.location.href,
180
+ };
181
+ this.logger.log(`iFrame Event - Iframe Queue thank-you ping (pageId=${pageId})`);
182
+ window.parent.postMessage(message, "*");
183
+ }
150
184
  sendIframeHeight() {
151
185
  let height = document.body.offsetHeight;
152
186
  this.logger.log("iFrame Event - Sending iFrame height of: " + height + "px"); // check the message is being sent correctly
package/dist/index.d.ts CHANGED
@@ -4,6 +4,7 @@ export { UpsellOptions, UpsellOptionsDefaults, } from "./interfaces/upsell-optio
4
4
  export { TranslateOptions, TranslateOptionsDefaults, } from "./interfaces/translate-options";
5
5
  export { ExitIntentOptions, ExitIntentOptionsDefaults, } from "./interfaces/exit-intent-options";
6
6
  export { FrequencyUpsellOptions, FrequencyUpsellOptionsDefaults, FrequencyUpsellABTestConfig, FrequencyUpsellConfig, } from "./interfaces/frequency-upsell-options";
7
+ export { IframeQueueOptions, IframeQueueOptionsDefaults, IframeQueueItem, IframeQueueErrorPayload, } from "./interfaces/iframe-queue-options";
7
8
  export * from "./loader";
8
9
  export * from "./app";
9
10
  export * from "./amount-label";
@@ -18,6 +19,7 @@ export * from "./click-to-expand";
18
19
  export * from "./advocacy";
19
20
  export * from "./data-attributes";
20
21
  export * from "./iframe";
22
+ export * from "./iframe-queue";
21
23
  export * from "./input-has-value-and-focus";
22
24
  export * from "./input-placeholders";
23
25
  export * from "./media-attribution";
package/dist/index.js CHANGED
@@ -4,6 +4,7 @@ export { UpsellOptionsDefaults, } from "./interfaces/upsell-options";
4
4
  export { TranslateOptionsDefaults, } from "./interfaces/translate-options";
5
5
  export { ExitIntentOptionsDefaults, } from "./interfaces/exit-intent-options";
6
6
  export { FrequencyUpsellOptionsDefaults, } from "./interfaces/frequency-upsell-options";
7
+ export { IframeQueueOptionsDefaults, } from "./interfaces/iframe-queue-options";
7
8
  export * from "./loader";
8
9
  export * from "./app";
9
10
  export * from "./amount-label";
@@ -18,6 +19,7 @@ export * from "./click-to-expand";
18
19
  export * from "./advocacy";
19
20
  export * from "./data-attributes";
20
21
  export * from "./iframe";
22
+ export * from "./iframe-queue";
21
23
  export * from "./input-has-value-and-focus";
22
24
  export * from "./input-placeholders";
23
25
  export * from "./media-attribution";
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Configuration interfaces for the Iframe Queue component.
3
+ *
4
+ * The Iframe Queue loads a sequence of embedded Engaging Networks pages
5
+ * one at a time, passes field values into them via `postMessage`, and
6
+ * exposes a global `IframeQueueEvents` instance so external code can
7
+ * subscribe to chain-completion. See iframe-queue.ts for the component.
8
+ *
9
+ * Configuration may be supplied either programmatically (via
10
+ * `IframeQueue.getInstance().enqueue(...).process()`) or declaratively
11
+ * by setting `window.EngridIframeQueue` on the host EN page before
12
+ * the ENgrid bundle loads.
13
+ */
14
+ /**
15
+ * A single embedded EN page to load and submit as part of the queue.
16
+ *
17
+ * The queue creates an iframe for each item, posts an
18
+ * `engrid-iframe-queue:populate` message to the iframe with the
19
+ * item's `fields`, and waits for an `engrid-iframe-queue:thank-you`
20
+ * ping (sent by the iFrame component on the embedded Thank You page)
21
+ * before advancing to the next item.
22
+ */
23
+ export interface IframeQueueItem {
24
+ /**
25
+ * Full URL of the EN page to load. The queue strips any `chain` query
26
+ * parameter defensively; do not rely on `?chain` for data passing.
27
+ */
28
+ url: string;
29
+ /**
30
+ * Field name → value pairs to populate in the embedded form via
31
+ * postMessage. Field names match the `name` attribute of the EN form
32
+ * input (e.g. `supporter.emailAddress`).
33
+ */
34
+ fields?: Record<string, string>;
35
+ /**
36
+ * If true, the embedded iframe submits the form once fields are
37
+ * populated. Default: true.
38
+ */
39
+ autoSubmit?: boolean;
40
+ /**
41
+ * Maximum time (ms) to wait for the Thank-You-page ping before
42
+ * treating the item as failed and aborting the queue. Default: 30000.
43
+ */
44
+ timeout?: number;
45
+ /**
46
+ * Element to append the iframe to. Default: document.body.
47
+ */
48
+ container?: HTMLElement;
49
+ /**
50
+ * Override iframe styles. Default: visually hidden
51
+ * (position:absolute; width:1px; height:1px; left:-9999px; opacity:0;).
52
+ */
53
+ iframeStyle?: Partial<CSSStyleDeclaration>;
54
+ /**
55
+ * Optional per-item callback fired when the item completes
56
+ * successfully. Useful for ad-hoc consumers that don't want to
57
+ * subscribe to the global IframeQueueEvents singleton.
58
+ */
59
+ onComplete?: () => void;
60
+ /**
61
+ * Optional per-item callback fired when the item fails (timeout,
62
+ * iframe load error, or message error from the embedded page).
63
+ */
64
+ onError?: (error: Error) => void;
65
+ /**
66
+ * If true, when this item fails (timeout / load error / embedded
67
+ * error message) the queue does NOT remove the iframe from the DOM
68
+ * and instead repositions it as a visible, framed overlay so a
69
+ * developer can inspect it (right-click → Inspect frame, view its
70
+ * console, etc.). Default: false.
71
+ *
72
+ * Independent of this flag, failed iframes are also kept and made
73
+ * visible automatically whenever ENgrid debug mode is on
74
+ * (`window.EngridOptions.Debug === true` or `?debug=true`).
75
+ */
76
+ keepIframeOnError?: boolean;
77
+ }
78
+ /**
79
+ * Payload dispatched by `IframeQueueEvents.onChainError` when the queue
80
+ * aborts due to an error. `failedItem` may be undefined if the error
81
+ * occurred before any item started processing.
82
+ */
83
+ export interface IframeQueueErrorPayload {
84
+ message: string;
85
+ failedItem?: IframeQueueItem;
86
+ cause?: Error;
87
+ }
88
+ /**
89
+ * Declarative configuration set on `window.EngridIframeQueue` to
90
+ * preload items and optionally auto-start the queue at page load.
91
+ */
92
+ export interface IframeQueueOptions {
93
+ /** Items to enqueue at startup. */
94
+ items?: IframeQueueItem[];
95
+ /**
96
+ * Whether to call `process()` automatically once items are enqueued
97
+ * at startup. Default: true if `items` is non-empty, else false.
98
+ */
99
+ autoStart?: boolean;
100
+ }
101
+ export declare const IframeQueueOptionsDefaults: IframeQueueOptions;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Configuration interfaces for the Iframe Queue component.
3
+ *
4
+ * The Iframe Queue loads a sequence of embedded Engaging Networks pages
5
+ * one at a time, passes field values into them via `postMessage`, and
6
+ * exposes a global `IframeQueueEvents` instance so external code can
7
+ * subscribe to chain-completion. See iframe-queue.ts for the component.
8
+ *
9
+ * Configuration may be supplied either programmatically (via
10
+ * `IframeQueue.getInstance().enqueue(...).process()`) or declaratively
11
+ * by setting `window.EngridIframeQueue` on the host EN page before
12
+ * the ENgrid bundle loads.
13
+ */
14
+ export const IframeQueueOptionsDefaults = {
15
+ items: [],
16
+ autoStart: true,
17
+ };
@@ -16,6 +16,7 @@ export interface Options {
16
16
  MinAmountMessage?: string;
17
17
  MaxAmountMessage?: string;
18
18
  UseAmountValidatorFromEN?: boolean;
19
+ DisableLiveValidation?: boolean;
19
20
  SkipToMainContentLink?: boolean;
20
21
  SrcDefer?: boolean;
21
22
  SuppressPurchaseEcard?: boolean;
@@ -6,6 +6,7 @@ export declare class MinMaxAmount {
6
6
  private maxAmount;
7
7
  private minAmountMessage;
8
8
  private maxAmountMessage;
9
+ private disableLiveValidation;
9
10
  private enAmountValidator;
10
11
  private logger;
11
12
  constructor();
@@ -10,6 +10,7 @@ export class MinMaxAmount {
10
10
  this.maxAmount = (_b = ENGrid.getOption("MaxAmount")) !== null && _b !== void 0 ? _b : 100000;
11
11
  this.minAmountMessage = ENGrid.getOption("MinAmountMessage");
12
12
  this.maxAmountMessage = ENGrid.getOption("MaxAmountMessage");
13
+ this.disableLiveValidation = ENGrid.getOption("DisableLiveValidation");
13
14
  this.enAmountValidator = null;
14
15
  this.logger = new EngridLogger("MinMaxAmount", "white", "purple", "🔢");
15
16
  if (!this.shouldRun()) {
@@ -17,8 +18,10 @@ export class MinMaxAmount {
17
18
  return;
18
19
  }
19
20
  this.setValidationConfigFromEN();
20
- this._amount.onAmountChange.subscribe((s) => window.setTimeout(this.liveValidate.bind(this), 1000) // Wait 1 second for the amount to be updated
21
- );
21
+ if (!this.disableLiveValidation) {
22
+ this._amount.onAmountChange.subscribe((s) => window.setTimeout(this.liveValidate.bind(this), 1000) // Wait 1 second for the amount to be updated
23
+ );
24
+ }
22
25
  this._form.onValidate.subscribe(this.enOnValidate.bind(this));
23
26
  }
24
27
  // Should we run the script?
@@ -36,6 +39,14 @@ export class MinMaxAmount {
36
39
  otherAmount.focus();
37
40
  }
38
41
  this._form.validate = false;
42
+ if (this.disableLiveValidation) {
43
+ this.logger.log("Setting error on enOnValidate: " +
44
+ (this.minAmountMessage || "Invalid Amount"));
45
+ // Defer so EN's own onValidate pass can't overwrite the error
46
+ window.setTimeout(() => {
47
+ ENGrid.setError(".en__field--withOther", this.minAmountMessage || "Invalid Amount");
48
+ }, 300);
49
+ }
39
50
  }
40
51
  else if (this._amount.amount > this.maxAmount) {
41
52
  this.logger.log("Amount is greater than max amount: " + this.maxAmount);
@@ -43,11 +54,29 @@ export class MinMaxAmount {
43
54
  otherAmount.focus();
44
55
  }
45
56
  this._form.validate = false;
57
+ if (this.disableLiveValidation) {
58
+ this.logger.log("Setting error on enOnValidate: " +
59
+ (this.maxAmountMessage || "Invalid Amount"));
60
+ // Defer so EN's own onValidate pass can't overwrite the error
61
+ window.setTimeout(() => {
62
+ ENGrid.setError(".en__field--withOther", this.maxAmountMessage || "Invalid Amount");
63
+ }, 300);
64
+ }
65
+ }
66
+ else if (this.disableLiveValidation) {
67
+ // Amount is in range — clear any stale error left over from a previous submit
68
+ ENGrid.removeError(".en__field--withOther");
69
+ }
70
+ if (!this.disableLiveValidation) {
71
+ window.setTimeout(this.liveValidate.bind(this), 300);
46
72
  }
47
- window.setTimeout(this.liveValidate.bind(this), 300);
48
73
  }
49
74
  // Disable Submit Button if the amount is not valid
50
75
  liveValidate() {
76
+ if (this.disableLiveValidation) {
77
+ this.logger.log("disableLiveValidation is set to true. Skipping live validation");
78
+ return;
79
+ }
51
80
  const amount = ENGrid.cleanAmount(this._amount.amount.toString());
52
81
  const activeElement = document.activeElement;
53
82
  if (activeElement &&
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const AppVersion = "0.25.1";
1
+ export declare const AppVersion = "0.25.5";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const AppVersion = "0.25.1";
1
+ export const AppVersion = "0.25.5";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@4site/engrid-scripts",
3
- "version": "0.25.1",
3
+ "version": "0.25.5",
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": "408f1d5237c46b5d530347056a2adedab3913739",
28
+ "gitHead": "f4a3b2759036e89ed5e19c907df1f2224d44f4bb",
29
29
  "devDependencies": {
30
30
  "nodemon": "^2.0.20"
31
31
  }