@windstream/react-shared-components 0.1.93 → 0.1.95

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.
Files changed (112) hide show
  1. package/dist/contentful/index.esm.js +2 -2
  2. package/dist/contentful/index.esm.js.map +1 -1
  3. package/dist/contentful/index.js +3 -3
  4. package/dist/contentful/index.js.map +1 -1
  5. package/dist/core.d.ts +2 -2
  6. package/dist/index.d.ts +2 -2
  7. package/dist/index.esm.js +1 -1
  8. package/dist/index.esm.js.map +1 -1
  9. package/dist/index.js +3 -3
  10. package/dist/index.js.map +1 -1
  11. package/dist/styles.css +1 -1
  12. package/dist/utils/index.esm.js +1 -1
  13. package/dist/utils/index.js +1 -1
  14. package/package.json +14 -8
  15. package/src/components/accordion/index.test.tsx +270 -0
  16. package/src/components/alert-card/index.test.tsx +152 -0
  17. package/src/components/animation-wrapper/index.test.tsx +424 -0
  18. package/src/components/brand-button/index.test.tsx +292 -0
  19. package/src/components/button/index.test.tsx +91 -0
  20. package/src/components/call-button/index.test.tsx +260 -0
  21. package/src/components/checkbox/index.test.tsx +252 -0
  22. package/src/components/checklist/index.test.tsx +231 -0
  23. package/src/components/checklist/index.tsx +64 -29
  24. package/src/components/checklist/types.ts +7 -1
  25. package/src/components/collapse/index.test.tsx +277 -0
  26. package/src/components/collapse/index.tsx +1 -0
  27. package/src/components/divider/index.test.tsx +53 -0
  28. package/src/components/image/index.test.tsx +174 -0
  29. package/src/components/input/index.test.tsx +348 -0
  30. package/src/components/link/index.test.tsx +199 -0
  31. package/src/components/list/index.test.tsx +166 -0
  32. package/src/components/material-icon/index.test.tsx +130 -0
  33. package/src/components/modal/index.test.tsx +310 -0
  34. package/src/components/next-image/index.test.tsx +406 -0
  35. package/src/components/pagination/index.test.tsx +521 -0
  36. package/src/components/radio-button/index.test.tsx +151 -0
  37. package/src/components/see-more/index.test.tsx +96 -0
  38. package/src/components/select/index.test.tsx +256 -0
  39. package/src/components/select-plan-button/index.test.tsx +173 -0
  40. package/src/components/skeleton/index.test.tsx +74 -0
  41. package/src/components/spinner/index.test.tsx +76 -0
  42. package/src/components/text/index.test.tsx +65 -0
  43. package/src/components/tooltip/index.test.tsx +50 -0
  44. package/src/components/view-cart-button/index.test.tsx +57 -0
  45. package/src/contentful/blocks/accordion/index.test.tsx +218 -0
  46. package/src/contentful/blocks/accordion/index.tsx +3 -1
  47. package/src/contentful/blocks/address-input-banner/index.test.tsx +132 -0
  48. package/src/contentful/blocks/anchored-bottom-banner/index.test.tsx +287 -0
  49. package/src/contentful/blocks/blogs-grid/BlogGrid.stories.tsx +5 -4
  50. package/src/contentful/blocks/blogs-grid/index.test.tsx +355 -0
  51. package/src/contentful/blocks/blogs-grid-base/index.test.tsx +274 -0
  52. package/src/contentful/blocks/breadcrumbs/index.test.tsx +281 -0
  53. package/src/contentful/blocks/button/index.test.tsx +339 -0
  54. package/src/contentful/blocks/callout/index.test.tsx +539 -0
  55. package/src/contentful/blocks/cards/blog-card/index.test.tsx +218 -0
  56. package/src/contentful/blocks/cards/floating-image-card/index.test.tsx +201 -0
  57. package/src/contentful/blocks/cards/full-image-card/index.test.tsx +216 -0
  58. package/src/contentful/blocks/cards/index.test.tsx +39 -0
  59. package/src/contentful/blocks/cards/product-card/index.test.tsx +263 -0
  60. package/src/contentful/blocks/cards/simple-card/index.test.tsx +364 -0
  61. package/src/contentful/blocks/cards/simple-card/index.tsx +1 -1
  62. package/src/contentful/blocks/cards/testimonial-card/index.test.tsx +180 -0
  63. package/src/contentful/blocks/carousel/helper.test.tsx +539 -0
  64. package/src/contentful/blocks/carousel/index.test.tsx +308 -0
  65. package/src/contentful/blocks/carousel/types.test.ts +16 -0
  66. package/src/contentful/blocks/cart-retention-banner/index.test.tsx +409 -0
  67. package/src/contentful/blocks/cart-retention-banner/index.tsx +4 -4
  68. package/src/contentful/blocks/comparison-table/index.test.tsx +114 -0
  69. package/src/contentful/blocks/cookiebanner/index.test.tsx +277 -0
  70. package/src/contentful/blocks/cta-callout/index.test.tsx +244 -0
  71. package/src/contentful/blocks/dynamic-tabs/index.test.tsx +240 -0
  72. package/src/contentful/blocks/email-input-block/index.test.tsx +213 -0
  73. package/src/contentful/blocks/email-input-block/index.tsx +40 -35
  74. package/src/contentful/blocks/find-kinetic/index.test.tsx +269 -0
  75. package/src/contentful/blocks/floating-banner/index.test.tsx +246 -0
  76. package/src/contentful/blocks/footer/index.test.tsx +302 -0
  77. package/src/contentful/blocks/image-promo-bar/helper.test.tsx +61 -0
  78. package/src/contentful/blocks/image-promo-bar/index.test.tsx +467 -0
  79. package/src/contentful/blocks/image-promo-bar/index.tsx +248 -246
  80. package/src/contentful/blocks/image-promo-bar/vimeo-embed.test.tsx +142 -0
  81. package/src/contentful/blocks/image-promo-bar/youtube-embed.test.tsx +104 -0
  82. package/src/contentful/blocks/modal/index.test.tsx +209 -0
  83. package/src/contentful/blocks/navigation/desktop-link-groups.tsx/index.test.tsx +208 -0
  84. package/src/contentful/blocks/navigation/index.test.tsx +924 -0
  85. package/src/contentful/blocks/navigation/mobile-link-groups.tsx/index.test.tsx +131 -0
  86. package/src/contentful/blocks/primary-hero/index.test.tsx +286 -0
  87. package/src/contentful/blocks/primary-hero/index.tsx +7 -4
  88. package/src/contentful/blocks/search-block/index.test.tsx +268 -0
  89. package/src/contentful/blocks/shape-background-wrapper/index.test.tsx +284 -0
  90. package/src/contentful/blocks/text/index.test.tsx +36 -0
  91. package/src/contentful/index.test.ts +45 -0
  92. package/src/global-mocks/contentful/to-document.ts +25 -0
  93. package/src/global-mocks/cookie.ts +48 -0
  94. package/src/global-mocks/cx.ts +37 -0
  95. package/src/global-mocks/index.ts +89 -0
  96. package/src/global-mocks/speed-card-bg.ts +27 -0
  97. package/src/global-mocks/utm.ts +49 -0
  98. package/src/hooks/contentful/use-contentful-rich-text.test.tsx +1758 -0
  99. package/src/hooks/contentful/use-contentful-rich-text.tsx +1 -1
  100. package/src/hooks/contentful/use-processed-check-list.test.tsx +277 -0
  101. package/src/hooks/use-body-scroll-lock.test.ts +134 -0
  102. package/src/hooks/use-carousel-swipe.test.ts +393 -0
  103. package/src/hooks/use-outside-click.test.ts +142 -0
  104. package/src/index.ts +1 -1
  105. package/src/next/index.test.ts +7 -0
  106. package/src/setupTests.ts +17 -11
  107. package/src/utils/contentful/to-document.test.ts +85 -0
  108. package/src/utils/cookie.test.ts +180 -0
  109. package/src/utils/cx.test.ts +90 -0
  110. package/src/utils/index.test.ts +115 -0
  111. package/src/utils/speed-card-bg.test.ts +46 -0
  112. package/src/utils/utm.test.ts +359 -0
@@ -0,0 +1,359 @@
1
+ import type { UTMProperties } from "../types/utm";
2
+ import {
3
+ buildPreservedQueryHref,
4
+ combineExistingAndNewUTMs,
5
+ getCampaignProperties,
6
+ getOrganicTrafficUtmParameters,
7
+ getUtmParametersFromURL,
8
+ } from "./utm";
9
+
10
+ describe("utm utilities", () => {
11
+ // ── getUtmParametersFromURL ──────────────────────────
12
+ describe("getUtmParametersFromURL", () => {
13
+ const origLocation = window.location;
14
+
15
+ afterEach(() => {
16
+ Object.defineProperty(window, "location", {
17
+ value: origLocation,
18
+ writable: true,
19
+ });
20
+ });
21
+
22
+ it("returns null when window is undefined", () => {
23
+ const origWindow = globalThis.window;
24
+ // @ts-expect-error - intentionally deleting window
25
+ delete globalThis.window;
26
+ expect(getUtmParametersFromURL()).toBeNull();
27
+ globalThis.window = origWindow;
28
+ });
29
+
30
+ it("returns null when no UTM params in URL", () => {
31
+ Object.defineProperty(window, "location", {
32
+ value: { search: "?foo=bar&baz=qux" },
33
+ writable: true,
34
+ });
35
+ expect(getUtmParametersFromURL()).toBeNull();
36
+ });
37
+
38
+ it("extracts all UTM params from URL", () => {
39
+ Object.defineProperty(window, "location", {
40
+ value: {
41
+ search:
42
+ "?utm_source=google&utm_medium=cpc&utm_campaign=spring&utm_term=shoes&utm_content=ad1&utm_campaign_id=c123&utm_adgroup_id=ag456&gclid=g789&fbclid=f012&msclkid=m345",
43
+ },
44
+ writable: true,
45
+ });
46
+ const result = getUtmParametersFromURL();
47
+ expect(result).toEqual({
48
+ utm_source: "google",
49
+ utm_medium: "cpc",
50
+ utm_campaign: "spring",
51
+ utm_term: "shoes",
52
+ utm_content: "ad1",
53
+ utm_campaign_id: "c123",
54
+ utm_adgroup_id: "ag456",
55
+ gclid: "g789",
56
+ fbclid: "f012",
57
+ msclkid: "m345",
58
+ });
59
+ });
60
+
61
+ it("extracts partial UTM params", () => {
62
+ Object.defineProperty(window, "location", {
63
+ value: { search: "?utm_source=bing&gclid=abc" },
64
+ writable: true,
65
+ });
66
+ const result = getUtmParametersFromURL();
67
+ expect(result).toEqual({ utm_source: "bing", gclid: "abc" });
68
+ });
69
+ });
70
+
71
+ // ── combineExistingAndNewUTMs ────────────────────────
72
+ describe("combineExistingAndNewUTMs", () => {
73
+ it("returns existing when incoming is null", () => {
74
+ const existing: UTMProperties = { utm_source: "google" };
75
+ expect(combineExistingAndNewUTMs(existing, null)).toEqual(existing);
76
+ });
77
+
78
+ it("returns empty object when both are null", () => {
79
+ expect(combineExistingAndNewUTMs(null, null)).toEqual({});
80
+ });
81
+
82
+ it("returns incoming when existing is null", () => {
83
+ const incoming: UTMProperties = { utm_source: "bing" };
84
+ expect(combineExistingAndNewUTMs(null, incoming)).toEqual(incoming);
85
+ });
86
+
87
+ it("preserves existing content/term for same campaign", () => {
88
+ const existing: UTMProperties = {
89
+ utm_campaign: "spring",
90
+ utm_source: "google",
91
+ utm_medium: "cpc",
92
+ utm_content: "original-ad",
93
+ utm_term: "original-keyword",
94
+ };
95
+ const incoming: UTMProperties = {
96
+ utm_campaign: "spring",
97
+ utm_source: "google",
98
+ utm_medium: "cpc",
99
+ utm_content: "new-ad",
100
+ utm_term: "new-keyword",
101
+ };
102
+ const result = combineExistingAndNewUTMs(existing, incoming);
103
+ expect(result.utm_content).toBe("original-ad");
104
+ expect(result.utm_term).toBe("original-keyword");
105
+ });
106
+
107
+ it("uses incoming content/term when existing has none (same campaign)", () => {
108
+ const existing: UTMProperties = {
109
+ utm_campaign: "spring",
110
+ utm_source: "google",
111
+ utm_medium: "cpc",
112
+ };
113
+ const incoming: UTMProperties = {
114
+ utm_campaign: "spring",
115
+ utm_source: "google",
116
+ utm_medium: "cpc",
117
+ utm_content: "new-ad",
118
+ utm_term: "new-keyword",
119
+ };
120
+ const result = combineExistingAndNewUTMs(existing, incoming);
121
+ expect(result.utm_content).toBe("new-ad");
122
+ expect(result.utm_term).toBe("new-keyword");
123
+ });
124
+
125
+ it("overwrites with incoming for different campaign", () => {
126
+ const existing: UTMProperties = {
127
+ utm_campaign: "spring",
128
+ utm_source: "google",
129
+ utm_medium: "cpc",
130
+ utm_content: "old-content",
131
+ utm_term: "old-term",
132
+ };
133
+ const incoming: UTMProperties = {
134
+ utm_campaign: "summer",
135
+ utm_source: "facebook",
136
+ utm_medium: "social",
137
+ utm_content: "new-content",
138
+ utm_term: "new-term",
139
+ };
140
+ const result = combineExistingAndNewUTMs(existing, incoming);
141
+ expect(result.utm_campaign).toBe("summer");
142
+ expect(result.utm_content).toBe("new-content");
143
+ expect(result.utm_term).toBe("new-term");
144
+ });
145
+
146
+ it("carries forward existing content/term if incoming doesnt have them (different campaign)", () => {
147
+ const existing: UTMProperties = {
148
+ utm_campaign: "spring",
149
+ utm_source: "google",
150
+ utm_medium: "cpc",
151
+ utm_content: "old-content",
152
+ utm_term: "old-term",
153
+ };
154
+ const incoming: UTMProperties = {
155
+ utm_campaign: "summer",
156
+ utm_source: "facebook",
157
+ utm_medium: "social",
158
+ };
159
+ const result = combineExistingAndNewUTMs(existing, incoming);
160
+ expect(result.utm_campaign).toBe("summer");
161
+ expect(result.utm_content).toBe("old-content");
162
+ expect(result.utm_term).toBe("old-term");
163
+ });
164
+ });
165
+
166
+ // ── getOrganicTrafficUtmParameters ───────────────────
167
+ describe("getOrganicTrafficUtmParameters", () => {
168
+ const origReferrer = Object.getOwnPropertyDescriptor(
169
+ Document.prototype,
170
+ "referrer"
171
+ );
172
+
173
+ function setReferrer(value: string) {
174
+ Object.defineProperty(document, "referrer", {
175
+ value,
176
+ configurable: true,
177
+ });
178
+ }
179
+
180
+ afterEach(() => {
181
+ if (origReferrer) {
182
+ Object.defineProperty(document, "referrer", origReferrer);
183
+ }
184
+ });
185
+
186
+ it("returns direct/direct when no referrer", () => {
187
+ setReferrer("");
188
+ expect(getOrganicTrafficUtmParameters()).toEqual({
189
+ utm_medium: "direct",
190
+ utm_source: "direct",
191
+ });
192
+ });
193
+
194
+ it("returns organic/google for Google referrer", () => {
195
+ setReferrer("https://www.google.com/search?q=kinetic");
196
+ expect(getOrganicTrafficUtmParameters()).toEqual({
197
+ utm_medium: "organic",
198
+ utm_source: "google",
199
+ });
200
+ });
201
+
202
+ it("returns organic/bing for Bing referrer", () => {
203
+ setReferrer("https://www.bing.com/search?q=kinetic");
204
+ expect(getOrganicTrafficUtmParameters()).toEqual({
205
+ utm_medium: "organic",
206
+ utm_source: "bing",
207
+ });
208
+ });
209
+
210
+ it("returns null for internal windstream.com referrer", () => {
211
+ setReferrer("https://www.windstream.com/plans");
212
+ expect(getOrganicTrafficUtmParameters()).toBeNull();
213
+ });
214
+
215
+ it("returns null for internal gokinetic.com referrer", () => {
216
+ setReferrer("https://www.gokinetic.com/checkout");
217
+ expect(getOrganicTrafficUtmParameters()).toBeNull();
218
+ });
219
+
220
+ it("returns referral for external referrer", () => {
221
+ setReferrer("https://reddit.com/r/internet");
222
+ expect(getOrganicTrafficUtmParameters()).toEqual({
223
+ utm_medium: "referral",
224
+ utm_source: "reddit.com",
225
+ });
226
+ });
227
+
228
+ it("returns direct/direct for invalid referrer URL", () => {
229
+ setReferrer("not-a-valid-url");
230
+ expect(getOrganicTrafficUtmParameters()).toEqual({
231
+ utm_medium: "direct",
232
+ utm_source: "direct",
233
+ });
234
+ });
235
+ });
236
+
237
+ // ── getCampaignProperties ────────────────────────────
238
+ describe("getCampaignProperties", () => {
239
+ it("returns null for null input", () => {
240
+ expect(getCampaignProperties(null)).toBeNull();
241
+ });
242
+
243
+ it("returns null for empty UTMs", () => {
244
+ expect(getCampaignProperties({})).toBeNull();
245
+ });
246
+
247
+ it("maps all UTM properties to campaign properties", () => {
248
+ const utms: UTMProperties = {
249
+ utm_campaign: "spring",
250
+ utm_source: "google",
251
+ utm_medium: "cpc",
252
+ utm_term: "shoes",
253
+ utm_content: "ad1",
254
+ utm_campaign_id: "c123",
255
+ utm_adgroup_id: "ag456",
256
+ gclid: "g789",
257
+ fbclid: "f012",
258
+ msclkid: "m345",
259
+ };
260
+ expect(getCampaignProperties(utms)).toEqual({
261
+ name: "spring",
262
+ source: "google",
263
+ medium: "cpc",
264
+ term: "shoes",
265
+ content: "ad1",
266
+ campaign_id: "c123",
267
+ adgroup_id: "ag456",
268
+ gclid: "g789",
269
+ fbclid: "f012",
270
+ msclkid: "m345",
271
+ });
272
+ });
273
+
274
+ it("maps partial UTMs", () => {
275
+ expect(
276
+ getCampaignProperties({ utm_source: "bing", utm_medium: "organic" })
277
+ ).toEqual({ source: "bing", medium: "organic" });
278
+ });
279
+ });
280
+
281
+ // ── buildPreservedQueryHref ──────────────────────────
282
+ describe("buildPreservedQueryHref", () => {
283
+ it("returns href unchanged when no current search", () => {
284
+ expect(buildPreservedQueryHref("/plans", "")).toBe("/plans");
285
+ });
286
+
287
+ it("strips UTM params from current search", () => {
288
+ const result = buildPreservedQueryHref(
289
+ "/plans",
290
+ "?utm_source=google&foo=bar"
291
+ );
292
+ expect(result).toContain("foo=bar");
293
+ expect(result).not.toContain("utm_source");
294
+ });
295
+
296
+ it("strips searchtext and page params", () => {
297
+ const result = buildPreservedQueryHref(
298
+ "/results",
299
+ "?searchtext=hello&page=3&category=fiber"
300
+ );
301
+ expect(result).toContain("category=fiber");
302
+ expect(result).not.toContain("searchtext");
303
+ expect(result).not.toContain("page=3");
304
+ });
305
+
306
+ it("preserves non-UTM params and merges with target", () => {
307
+ const result = buildPreservedQueryHref(
308
+ "/plans?speed=500",
309
+ "?promo=SAVE20"
310
+ );
311
+ expect(result).toContain("speed=500");
312
+ expect(result).toContain("promo=SAVE20");
313
+ });
314
+
315
+ it("handles absolute URLs", () => {
316
+ const result = buildPreservedQueryHref(
317
+ "https://gokinetic.com/plans?speed=500",
318
+ "?promo=SAVE20"
319
+ );
320
+ expect(result).toContain("https://gokinetic.com/plans");
321
+ expect(result).toContain("speed=500");
322
+ expect(result).toContain("promo=SAVE20");
323
+ });
324
+
325
+ it("does not duplicate params already in target", () => {
326
+ const result = buildPreservedQueryHref(
327
+ "/plans?promo=SAVE20",
328
+ "?promo=OLD"
329
+ );
330
+ const params = new URLSearchParams(result.split("?")[1]);
331
+ expect(params.getAll("promo")).toHaveLength(1);
332
+ expect(params.get("promo")).toBe("SAVE20");
333
+ });
334
+
335
+ it("returns href for invalid URL when href is not parseable", () => {
336
+ // The catch block handles invalid absolute-looking URLs
337
+ // Relative paths are handled by the else branch, so test a truly invalid URL
338
+ const result = buildPreservedQueryHref("http://[invalid", "?foo=bar");
339
+ expect(result).toBe("http://[invalid");
340
+ });
341
+
342
+ it("returns basePath without query string when all params stripped", () => {
343
+ const result = buildPreservedQueryHref(
344
+ "/plans",
345
+ "?utm_source=google&utm_medium=cpc"
346
+ );
347
+ expect(result).toBe("/plans");
348
+ });
349
+
350
+ it("handles relative path with existing query", () => {
351
+ const result = buildPreservedQueryHref(
352
+ "/plans?existing=value",
353
+ "?extra=param"
354
+ );
355
+ expect(result).toContain("existing=value");
356
+ expect(result).toContain("extra=param");
357
+ });
358
+ });
359
+ });