@windstream/react-shared-components 0.1.92 → 0.1.94

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 (293) hide show
  1. package/README.md +635 -635
  2. package/dist/contentful/index.d.ts +1 -0
  3. package/dist/contentful/index.esm.js +2 -2
  4. package/dist/contentful/index.esm.js.map +1 -1
  5. package/dist/contentful/index.js +3 -3
  6. package/dist/contentful/index.js.map +1 -1
  7. package/dist/core.d.ts +5 -5
  8. package/dist/index.d.ts +2 -2
  9. package/dist/index.esm.js +1 -1
  10. package/dist/index.esm.js.map +1 -1
  11. package/dist/index.js +1 -1
  12. package/dist/index.js.map +1 -1
  13. package/dist/styles.css +1 -1
  14. package/dist/utils/index.esm.js +1 -1
  15. package/dist/utils/index.js +1 -1
  16. package/package.json +191 -185
  17. package/src/components/accordion/Accordion.stories.tsx +230 -230
  18. package/src/components/accordion/index.test.tsx +270 -0
  19. package/src/components/accordion/index.tsx +70 -70
  20. package/src/components/accordion/types.ts +12 -12
  21. package/src/components/alert-card/AlertCard.stories.tsx +171 -171
  22. package/src/components/alert-card/index.test.tsx +152 -0
  23. package/src/components/alert-card/index.tsx +41 -41
  24. package/src/components/alert-card/types.ts +13 -13
  25. package/src/components/animation-wrapper/index.test.tsx +424 -0
  26. package/src/components/animation-wrapper/index.tsx +129 -129
  27. package/src/components/animation-wrapper/types.ts +11 -11
  28. package/src/components/brand-button/BrandButton.stories.tsx +223 -223
  29. package/src/components/brand-button/helpers.ts +35 -35
  30. package/src/components/brand-button/index.test.tsx +292 -0
  31. package/src/components/brand-button/index.tsx +120 -120
  32. package/src/components/brand-button/types.ts +38 -38
  33. package/src/components/button/Button.stories.tsx +108 -108
  34. package/src/components/button/index.test.tsx +91 -0
  35. package/src/components/button/index.tsx +27 -27
  36. package/src/components/button/types.ts +14 -14
  37. package/src/components/call-button/CallButton.stories.tsx +324 -324
  38. package/src/components/call-button/index.test.tsx +260 -0
  39. package/src/components/call-button/index.tsx +106 -106
  40. package/src/components/call-button/types.ts +16 -16
  41. package/src/components/checkbox/Checkbox.stories.tsx +247 -247
  42. package/src/components/checkbox/index.test.tsx +252 -0
  43. package/src/components/checkbox/index.tsx +197 -197
  44. package/src/components/checkbox/types.ts +27 -27
  45. package/src/components/checklist/Checklist.stories.tsx +150 -150
  46. package/src/components/checklist/index.test.tsx +231 -0
  47. package/src/components/checklist/index.tsx +96 -61
  48. package/src/components/checklist/types.ts +23 -17
  49. package/src/components/collapse/Collapse.stories.tsx +255 -255
  50. package/src/components/collapse/index.test.tsx +277 -0
  51. package/src/components/collapse/index.tsx +47 -46
  52. package/src/components/collapse/types.ts +6 -6
  53. package/src/components/divider/Divider.stories.tsx +205 -205
  54. package/src/components/divider/index.test.tsx +53 -0
  55. package/src/components/divider/index.tsx +22 -22
  56. package/src/components/divider/type.ts +3 -3
  57. package/src/components/image/Image.stories.tsx +113 -113
  58. package/src/components/image/index.test.tsx +174 -0
  59. package/src/components/image/index.tsx +25 -25
  60. package/src/components/image/types.ts +40 -40
  61. package/src/components/input/Input.stories.tsx +325 -325
  62. package/src/components/input/index.test.tsx +348 -0
  63. package/src/components/input/index.tsx +177 -177
  64. package/src/components/input/types.ts +37 -37
  65. package/src/components/link/Link.stories.tsx +163 -163
  66. package/src/components/link/index.test.tsx +199 -0
  67. package/src/components/link/index.tsx +116 -116
  68. package/src/components/link/types.ts +25 -25
  69. package/src/components/list/List.stories.tsx +272 -272
  70. package/src/components/list/index.test.tsx +166 -0
  71. package/src/components/list/index.tsx +88 -88
  72. package/src/components/list/list-item/index.tsx +38 -38
  73. package/src/components/list/list-item/types.ts +13 -13
  74. package/src/components/list/types.ts +29 -29
  75. package/src/components/material-icon/MaterialIcon.stories.tsx +322 -322
  76. package/src/components/material-icon/constants.ts +99 -99
  77. package/src/components/material-icon/index.test.tsx +130 -0
  78. package/src/components/material-icon/index.tsx +47 -47
  79. package/src/components/material-icon/types.ts +31 -31
  80. package/src/components/modal/Modal.stories.tsx +171 -171
  81. package/src/components/modal/index.test.tsx +310 -0
  82. package/src/components/modal/index.tsx +164 -164
  83. package/src/components/modal/types.ts +24 -24
  84. package/src/components/next-image/index.test.tsx +406 -0
  85. package/src/components/next-image/index.tsx +74 -74
  86. package/src/components/next-image/types.ts +1 -1
  87. package/src/components/pagination/index.test.tsx +521 -0
  88. package/src/components/pagination/index.tsx +91 -91
  89. package/src/components/pagination/types.ts +6 -6
  90. package/src/components/radio-button/RadioButton.stories.tsx +307 -307
  91. package/src/components/radio-button/index.test.tsx +151 -0
  92. package/src/components/radio-button/index.tsx +75 -75
  93. package/src/components/radio-button/types.ts +21 -21
  94. package/src/components/see-more/SeeMore.stories.tsx +181 -181
  95. package/src/components/see-more/index.test.tsx +96 -0
  96. package/src/components/see-more/index.tsx +44 -44
  97. package/src/components/see-more/types.ts +4 -4
  98. package/src/components/select/Select.stories.tsx +411 -411
  99. package/src/components/select/index.test.tsx +256 -0
  100. package/src/components/select/index.tsx +155 -155
  101. package/src/components/select/types.ts +36 -36
  102. package/src/components/select-plan-button/SelectPlanButton.stories.tsx +184 -184
  103. package/src/components/select-plan-button/index.test.tsx +173 -0
  104. package/src/components/select-plan-button/index.tsx +63 -63
  105. package/src/components/select-plan-button/types.ts +17 -17
  106. package/src/components/skeleton/Skeleton.stories.tsx +179 -179
  107. package/src/components/skeleton/index.test.tsx +74 -0
  108. package/src/components/skeleton/index.tsx +61 -61
  109. package/src/components/skeleton/types.ts +4 -4
  110. package/src/components/spinner/Spinner.stories.tsx +335 -335
  111. package/src/components/spinner/index.test.tsx +76 -0
  112. package/src/components/spinner/index.tsx +44 -44
  113. package/src/components/spinner/types.ts +5 -5
  114. package/src/components/text/Text.stories.tsx +321 -321
  115. package/src/components/text/index.test.tsx +65 -0
  116. package/src/components/text/index.tsx +25 -25
  117. package/src/components/text/types.ts +45 -45
  118. package/src/components/tooltip/Tooltip.stories.tsx +219 -219
  119. package/src/components/tooltip/index.test.tsx +50 -0
  120. package/src/components/tooltip/index.tsx +74 -74
  121. package/src/components/tooltip/types.ts +7 -7
  122. package/src/components/view-cart-button/ViewCartButton.stories.tsx +252 -252
  123. package/src/components/view-cart-button/index.test.tsx +57 -0
  124. package/src/components/view-cart-button/index.tsx +42 -42
  125. package/src/components/view-cart-button/types.ts +5 -5
  126. package/src/contentful/blocks/accordion/Accordion.stories.mocks.tsx +128 -128
  127. package/src/contentful/blocks/accordion/Accordion.stories.tsx +98 -98
  128. package/src/contentful/blocks/accordion/index.test.tsx +218 -0
  129. package/src/contentful/blocks/accordion/index.tsx +114 -112
  130. package/src/contentful/blocks/accordion/types.ts +34 -34
  131. package/src/contentful/blocks/address-input-banner/index.test.tsx +132 -0
  132. package/src/contentful/blocks/address-input-banner/index.tsx +52 -52
  133. package/src/contentful/blocks/address-input-banner/types.ts +14 -14
  134. package/src/contentful/blocks/anchored-bottom-banner/index.test.tsx +287 -0
  135. package/src/contentful/blocks/anchored-bottom-banner/index.tsx +181 -181
  136. package/src/contentful/blocks/anchored-bottom-banner/types.ts +13 -13
  137. package/src/contentful/blocks/blogs-grid/BlogGrid.stories.mocks.tsx +144 -144
  138. package/src/contentful/blocks/blogs-grid/BlogGrid.stories.tsx +157 -156
  139. package/src/contentful/blocks/blogs-grid/index.test.tsx +355 -0
  140. package/src/contentful/blocks/blogs-grid/index.tsx +134 -134
  141. package/src/contentful/blocks/blogs-grid/types.ts +26 -26
  142. package/src/contentful/blocks/blogs-grid-base/index.test.tsx +274 -0
  143. package/src/contentful/blocks/blogs-grid-base/index.tsx +119 -119
  144. package/src/contentful/blocks/blogs-grid-base/types.ts +36 -36
  145. package/src/contentful/blocks/breadcrumbs/BreadcrumbNavigation.stories.tsx +147 -147
  146. package/src/contentful/blocks/breadcrumbs/index.test.tsx +281 -0
  147. package/src/contentful/blocks/breadcrumbs/index.tsx +95 -95
  148. package/src/contentful/blocks/breadcrumbs/types.ts +8 -8
  149. package/src/contentful/blocks/button/Button.stories.tsx +40 -40
  150. package/src/contentful/blocks/button/index.test.tsx +339 -0
  151. package/src/contentful/blocks/button/index.tsx +131 -131
  152. package/src/contentful/blocks/button/types.ts +39 -39
  153. package/src/contentful/blocks/callout/Callout.stories.tsx +23 -23
  154. package/src/contentful/blocks/callout/index.test.tsx +539 -0
  155. package/src/contentful/blocks/callout/index.tsx +277 -277
  156. package/src/contentful/blocks/callout/types.ts +78 -78
  157. package/src/contentful/blocks/cards/Cards.stories.tsx +23 -23
  158. package/src/contentful/blocks/cards/blog-card/index.test.tsx +218 -0
  159. package/src/contentful/blocks/cards/blog-card/index.tsx +129 -129
  160. package/src/contentful/blocks/cards/blog-card/types.ts +34 -34
  161. package/src/contentful/blocks/cards/floating-image-card/index.test.tsx +201 -0
  162. package/src/contentful/blocks/cards/floating-image-card/index.tsx +119 -119
  163. package/src/contentful/blocks/cards/floating-image-card/types.ts +30 -30
  164. package/src/contentful/blocks/cards/full-image-card/index.test.tsx +216 -0
  165. package/src/contentful/blocks/cards/full-image-card/index.tsx +130 -130
  166. package/src/contentful/blocks/cards/full-image-card/types.ts +29 -29
  167. package/src/contentful/blocks/cards/index.test.tsx +39 -0
  168. package/src/contentful/blocks/cards/index.tsx +13 -13
  169. package/src/contentful/blocks/cards/product-card/index.test.tsx +263 -0
  170. package/src/contentful/blocks/cards/product-card/index.tsx +251 -251
  171. package/src/contentful/blocks/cards/product-card/types.ts +28 -28
  172. package/src/contentful/blocks/cards/simple-card/index.test.tsx +364 -0
  173. package/src/contentful/blocks/cards/simple-card/index.tsx +325 -325
  174. package/src/contentful/blocks/cards/simple-card/types.ts +71 -71
  175. package/src/contentful/blocks/cards/testimonial-card/index.test.tsx +180 -0
  176. package/src/contentful/blocks/cards/testimonial-card/index.tsx +90 -90
  177. package/src/contentful/blocks/cards/testimonial-card/types.tsx +12 -12
  178. package/src/contentful/blocks/cards/types.ts +1 -1
  179. package/src/contentful/blocks/carousel/Carousel.stories.tsx +23 -23
  180. package/src/contentful/blocks/carousel/helper.test.tsx +539 -0
  181. package/src/contentful/blocks/carousel/helper.tsx +494 -494
  182. package/src/contentful/blocks/carousel/index.test.tsx +308 -0
  183. package/src/contentful/blocks/carousel/index.tsx +87 -87
  184. package/src/contentful/blocks/carousel/types.test.ts +16 -0
  185. package/src/contentful/blocks/carousel/types.ts +145 -145
  186. package/src/contentful/blocks/cart-retention-banner/index.test.tsx +409 -0
  187. package/src/contentful/blocks/cart-retention-banner/index.tsx +109 -109
  188. package/src/contentful/blocks/cart-retention-banner/types.ts +11 -11
  189. package/src/contentful/blocks/comparison-table/index.test.tsx +114 -0
  190. package/src/contentful/blocks/comparison-table/index.tsx +29 -29
  191. package/src/contentful/blocks/comparison-table/types.ts +6 -6
  192. package/src/contentful/blocks/cookiebanner/index.test.tsx +277 -0
  193. package/src/contentful/blocks/cookiebanner/index.tsx +146 -146
  194. package/src/contentful/blocks/cookiebanner/type.ts +7 -7
  195. package/src/contentful/blocks/cta-callout/CtaCallout.stories.tsx +46 -46
  196. package/src/contentful/blocks/cta-callout/index.test.tsx +244 -0
  197. package/src/contentful/blocks/cta-callout/index.tsx +73 -73
  198. package/src/contentful/blocks/cta-callout/types.ts +26 -26
  199. package/src/contentful/blocks/dynamic-tabs/index.test.tsx +240 -0
  200. package/src/contentful/blocks/dynamic-tabs/index.tsx +204 -204
  201. package/src/contentful/blocks/dynamic-tabs/types.ts +21 -21
  202. package/src/contentful/blocks/email-input-block/index.test.tsx +213 -0
  203. package/src/contentful/blocks/email-input-block/index.tsx +121 -116
  204. package/src/contentful/blocks/email-input-block/types.ts +16 -16
  205. package/src/contentful/blocks/find-kinetic/FindKinetic.stories.tsx +23 -23
  206. package/src/contentful/blocks/find-kinetic/index.test.tsx +269 -0
  207. package/src/contentful/blocks/find-kinetic/index.tsx +138 -130
  208. package/src/contentful/blocks/find-kinetic/types.ts +20 -19
  209. package/src/contentful/blocks/floating-banner/FloatingBanner.stories.tsx +34 -34
  210. package/src/contentful/blocks/floating-banner/index.test.tsx +246 -0
  211. package/src/contentful/blocks/floating-banner/index.tsx +97 -97
  212. package/src/contentful/blocks/floating-banner/types.ts +22 -22
  213. package/src/contentful/blocks/footer/Footer.stories.tsx +317 -317
  214. package/src/contentful/blocks/footer/index.test.tsx +302 -0
  215. package/src/contentful/blocks/footer/index.tsx +91 -91
  216. package/src/contentful/blocks/footer/types.ts +13 -13
  217. package/src/contentful/blocks/image-promo-bar/ImagePromoBar.stories.tsx +23 -23
  218. package/src/contentful/blocks/image-promo-bar/helper.test.tsx +61 -0
  219. package/src/contentful/blocks/image-promo-bar/helper.tsx +28 -28
  220. package/src/contentful/blocks/image-promo-bar/index.test.tsx +467 -0
  221. package/src/contentful/blocks/image-promo-bar/index.tsx +246 -246
  222. package/src/contentful/blocks/image-promo-bar/types.ts +44 -44
  223. package/src/contentful/blocks/image-promo-bar/vimeo-embed.test.tsx +142 -0
  224. package/src/contentful/blocks/image-promo-bar/vimeo-embed.tsx +93 -93
  225. package/src/contentful/blocks/image-promo-bar/youtube-embed.test.tsx +104 -0
  226. package/src/contentful/blocks/image-promo-bar/youtube-embed.tsx +46 -46
  227. package/src/contentful/blocks/modal/constants.ts +53 -53
  228. package/src/contentful/blocks/modal/index.test.tsx +209 -0
  229. package/src/contentful/blocks/modal/index.tsx +108 -108
  230. package/src/contentful/blocks/modal/types.ts +12 -12
  231. package/src/contentful/blocks/navigation/Navigation.stories.mocks.tsx +78 -0
  232. package/src/contentful/blocks/navigation/Navigation.stories.tsx +138 -0
  233. package/src/contentful/blocks/navigation/desktop-link-groups.tsx/index.test.tsx +208 -0
  234. package/src/contentful/blocks/navigation/desktop-link-groups.tsx/index.tsx +141 -139
  235. package/src/contentful/blocks/navigation/index.test.tsx +924 -0
  236. package/src/contentful/blocks/navigation/index.tsx +569 -568
  237. package/src/contentful/blocks/navigation/mobile-link-groups.tsx/index.test.tsx +131 -0
  238. package/src/contentful/blocks/navigation/mobile-link-groups.tsx/index.tsx +82 -82
  239. package/src/contentful/blocks/navigation/types.ts +71 -71
  240. package/src/contentful/blocks/primary-hero/PrimaryHero.stories.tsx +23 -23
  241. package/src/contentful/blocks/primary-hero/index.test.tsx +286 -0
  242. package/src/contentful/blocks/primary-hero/index.tsx +239 -236
  243. package/src/contentful/blocks/primary-hero/types.ts +37 -37
  244. package/src/contentful/blocks/search-block/index.test.tsx +268 -0
  245. package/src/contentful/blocks/search-block/index.tsx +90 -90
  246. package/src/contentful/blocks/search-block/types.ts +15 -15
  247. package/src/contentful/blocks/shape-background-wrapper/ShapeBackgroundWrapper.stories.tsx +26 -26
  248. package/src/contentful/blocks/shape-background-wrapper/index.test.tsx +284 -0
  249. package/src/contentful/blocks/shape-background-wrapper/index.tsx +124 -124
  250. package/src/contentful/blocks/shape-background-wrapper/types.ts +36 -36
  251. package/src/contentful/blocks/text/Text.stories.tsx +23 -23
  252. package/src/contentful/blocks/text/index.test.tsx +36 -0
  253. package/src/contentful/blocks/text/index.tsx +12 -12
  254. package/src/contentful/blocks/text/types.ts +1 -1
  255. package/src/contentful/index.test.ts +45 -0
  256. package/src/contentful/index.ts +105 -105
  257. package/src/global-mocks/contentful/to-document.ts +25 -0
  258. package/src/global-mocks/cookie.ts +48 -0
  259. package/src/global-mocks/cx.ts +37 -0
  260. package/src/global-mocks/index.ts +89 -0
  261. package/src/global-mocks/speed-card-bg.ts +27 -0
  262. package/src/global-mocks/utm.ts +49 -0
  263. package/src/hooks/contentful/use-contentful-rich-text.test.tsx +1758 -0
  264. package/src/hooks/contentful/use-contentful-rich-text.tsx +309 -309
  265. package/src/hooks/contentful/use-processed-check-list.test.tsx +277 -0
  266. package/src/hooks/contentful/use-processed-check-list.ts +63 -63
  267. package/src/hooks/use-body-scroll-lock.test.ts +134 -0
  268. package/src/hooks/use-body-scroll-lock.ts +34 -34
  269. package/src/hooks/use-carousel-swipe.test.ts +393 -0
  270. package/src/hooks/use-carousel-swipe.ts +264 -264
  271. package/src/hooks/use-outside-click.test.ts +142 -0
  272. package/src/hooks/use-outside-click.ts +17 -17
  273. package/src/index.ts +107 -107
  274. package/src/next/index.test.ts +7 -0
  275. package/src/next/index.ts +5 -5
  276. package/src/setupTests.ts +52 -46
  277. package/src/stories/DocsTemplate.tsx +24 -24
  278. package/src/styles/globals.css +343 -343
  279. package/src/types/global.d.ts +9 -9
  280. package/src/types/micro-components.ts +99 -99
  281. package/src/types/utm.ts +49 -49
  282. package/src/utils/contentful/to-document.test.ts +85 -0
  283. package/src/utils/contentful/to-document.ts +24 -24
  284. package/src/utils/cookie.test.ts +180 -0
  285. package/src/utils/cookie.ts +84 -84
  286. package/src/utils/cx.test.ts +90 -0
  287. package/src/utils/cx.ts +49 -49
  288. package/src/utils/index.test.ts +115 -0
  289. package/src/utils/index.ts +41 -41
  290. package/src/utils/speed-card-bg.test.ts +46 -0
  291. package/src/utils/speed-card-bg.ts +24 -24
  292. package/src/utils/utm.test.ts +359 -0
  293. package/src/utils/utm.ts +221 -221
@@ -1,145 +1,145 @@
1
- import React, { ReactNode } from "react";
2
-
3
- import { CheckPlansProps } from "@shared/types/micro-components";
4
-
5
- export interface carouselFieldsInterface {
6
- __typename: "ComponentCarousel";
7
- sys: {
8
- id: string;
9
- };
10
- entryName?: string;
11
- anchorId?: string;
12
- title?: string;
13
- subTitle?: string;
14
- rotationTiming?: number;
15
- showArrows?: boolean;
16
- autoRotate?: boolean;
17
- initialSlideIndex?: number;
18
- infiniteScroll?: boolean;
19
- mobileNavigationType?: boolean;
20
- disclaimerText?: {
21
- json: any;
22
- };
23
- backgroundColor?: "blue" | "green" | "orange" | "purple" | "white";
24
- items: {
25
- items: Array<CarouselItemsType>;
26
- };
27
- }
28
-
29
- export type CarouselItemsType =
30
- | TestimonialCardFields
31
- | ProductCardFields
32
- | CardFields;
33
-
34
- export interface TestimonialCardFields {
35
- __typename: "ComponentTestimonialCard";
36
- sys: {
37
- id: string;
38
- };
39
- title?: string;
40
- author?: string;
41
- role?: string;
42
- rating?: number;
43
- quote?: ReactNode;
44
- authorImage?: {
45
- url: string;
46
- title?: string;
47
- width?: number;
48
- height?: number;
49
- } | null;
50
- avatarurl?: string;
51
- }
52
-
53
- export interface ProductCardFields {
54
- __typename: "ComponentProductCard";
55
- sys: {
56
- id: string;
57
- };
58
- speed?: string;
59
- price?: string;
60
- priceSuffix?: string;
61
- techType?: string;
62
- highlighted?: boolean;
63
- productCardDescription?: string;
64
- benefitsTitle?: string;
65
- benefitsExpanded?: boolean;
66
- innerBadge?: string;
67
- productCategory?: string;
68
- // GraphQL patterns for nested references
69
- benefits?: {
70
- items: Array<any>;
71
- };
72
- giftRewards?: {
73
- list?: {
74
- items: Array<any>;
75
- };
76
- };
77
- cta?: {
78
- buttonLabel?: string;
79
- [key: string]: any;
80
- };
81
- innerBadgeIcon?: {
82
- url: string;
83
- [key: string]: any;
84
- };
85
- }
86
-
87
- export interface CardFields {
88
- __typename: "ComponentCard";
89
- sys: {
90
- id: string;
91
- };
92
- title?: string;
93
- subtitle?: string;
94
- eyebrow?: string;
95
- body?: { json: any };
96
- image?: { url: string; [key: string]: any };
97
- cta?: { buttonLabel?: string; [key: string]: any };
98
- ctaBottom?: { buttonLabel?: string; [key: string]: any };
99
- }
100
-
101
- export interface CarouselWithTestimonialCards
102
- extends Omit<carouselFieldsInterface, "items"> {
103
- items: {
104
- items: TestimonialCardFields[];
105
- };
106
- }
107
-
108
- export interface CarouselWithProductCards
109
- extends Omit<carouselFieldsInterface, "items"> {
110
- items: {
111
- items: ProductCardFields[];
112
- };
113
- }
114
-
115
- export const backgroundColorMap = {
116
- blue: "bg-bg-fill-inverse",
117
- green: "bg-border-success",
118
- orange: "bg-orange-500",
119
- purple: "bg-purple-500",
120
- white: "bg-white",
121
- };
122
-
123
- export type CarouselProps = {
124
- fields: carouselFieldsInterface;
125
- hasTestimonialCards: boolean;
126
- hasProductCards: boolean;
127
- disclaimerText: ReactNode;
128
- backgroundColor: string;
129
- activeTab: string;
130
- setActiveTab: (tab: string) => void;
131
- tabs: string[];
132
- showSwitch?: boolean;
133
- testimonialAutoScroll?: boolean;
134
- onModalButtonClick?: (id?: string) => void;
135
- renderCheckPlans?: (overrides?: CheckPlansProps) => React.ReactNode;
136
- };
137
-
138
- export interface TabSwitchProps {
139
- tabs: string[];
140
- activeTab: string;
141
- onChange: (tab: string) => void;
142
- className?: string;
143
- }
144
-
145
- export const DEFAULT_TABS = ["Business Ready", "Internet Only"];
1
+ import React, { ReactNode } from "react";
2
+
3
+ import { CheckPlansProps } from "@shared/types/micro-components";
4
+
5
+ export interface carouselFieldsInterface {
6
+ __typename: "ComponentCarousel";
7
+ sys: {
8
+ id: string;
9
+ };
10
+ entryName?: string;
11
+ anchorId?: string;
12
+ title?: string;
13
+ subTitle?: string;
14
+ rotationTiming?: number;
15
+ showArrows?: boolean;
16
+ autoRotate?: boolean;
17
+ initialSlideIndex?: number;
18
+ infiniteScroll?: boolean;
19
+ mobileNavigationType?: boolean;
20
+ disclaimerText?: {
21
+ json: any;
22
+ };
23
+ backgroundColor?: "blue" | "green" | "orange" | "purple" | "white";
24
+ items: {
25
+ items: Array<CarouselItemsType>;
26
+ };
27
+ }
28
+
29
+ export type CarouselItemsType =
30
+ | TestimonialCardFields
31
+ | ProductCardFields
32
+ | CardFields;
33
+
34
+ export interface TestimonialCardFields {
35
+ __typename: "ComponentTestimonialCard";
36
+ sys: {
37
+ id: string;
38
+ };
39
+ title?: string;
40
+ author?: string;
41
+ role?: string;
42
+ rating?: number;
43
+ quote?: ReactNode;
44
+ authorImage?: {
45
+ url: string;
46
+ title?: string;
47
+ width?: number;
48
+ height?: number;
49
+ } | null;
50
+ avatarurl?: string;
51
+ }
52
+
53
+ export interface ProductCardFields {
54
+ __typename: "ComponentProductCard";
55
+ sys: {
56
+ id: string;
57
+ };
58
+ speed?: string;
59
+ price?: string;
60
+ priceSuffix?: string;
61
+ techType?: string;
62
+ highlighted?: boolean;
63
+ productCardDescription?: string;
64
+ benefitsTitle?: string;
65
+ benefitsExpanded?: boolean;
66
+ innerBadge?: string;
67
+ productCategory?: string;
68
+ // GraphQL patterns for nested references
69
+ benefits?: {
70
+ items: Array<any>;
71
+ };
72
+ giftRewards?: {
73
+ list?: {
74
+ items: Array<any>;
75
+ };
76
+ };
77
+ cta?: {
78
+ buttonLabel?: string;
79
+ [key: string]: any;
80
+ };
81
+ innerBadgeIcon?: {
82
+ url: string;
83
+ [key: string]: any;
84
+ };
85
+ }
86
+
87
+ export interface CardFields {
88
+ __typename: "ComponentCard";
89
+ sys: {
90
+ id: string;
91
+ };
92
+ title?: string;
93
+ subtitle?: string;
94
+ eyebrow?: string;
95
+ body?: { json: any };
96
+ image?: { url: string; [key: string]: any };
97
+ cta?: { buttonLabel?: string; [key: string]: any };
98
+ ctaBottom?: { buttonLabel?: string; [key: string]: any };
99
+ }
100
+
101
+ export interface CarouselWithTestimonialCards
102
+ extends Omit<carouselFieldsInterface, "items"> {
103
+ items: {
104
+ items: TestimonialCardFields[];
105
+ };
106
+ }
107
+
108
+ export interface CarouselWithProductCards
109
+ extends Omit<carouselFieldsInterface, "items"> {
110
+ items: {
111
+ items: ProductCardFields[];
112
+ };
113
+ }
114
+
115
+ export const backgroundColorMap = {
116
+ blue: "bg-bg-fill-inverse",
117
+ green: "bg-border-success",
118
+ orange: "bg-orange-500",
119
+ purple: "bg-purple-500",
120
+ white: "bg-white",
121
+ };
122
+
123
+ export type CarouselProps = {
124
+ fields: carouselFieldsInterface;
125
+ hasTestimonialCards: boolean;
126
+ hasProductCards: boolean;
127
+ disclaimerText: ReactNode;
128
+ backgroundColor: string;
129
+ activeTab: string;
130
+ setActiveTab: (tab: string) => void;
131
+ tabs: string[];
132
+ showSwitch?: boolean;
133
+ testimonialAutoScroll?: boolean;
134
+ onModalButtonClick?: (id?: string) => void;
135
+ renderCheckPlans?: (overrides?: CheckPlansProps) => React.ReactNode;
136
+ };
137
+
138
+ export interface TabSwitchProps {
139
+ tabs: string[];
140
+ activeTab: string;
141
+ onChange: (tab: string) => void;
142
+ className?: string;
143
+ }
144
+
145
+ export const DEFAULT_TABS = ["Business Ready", "Internet Only"];
@@ -0,0 +1,409 @@
1
+ import "@testing-library/jest-dom";
2
+
3
+ import CartRetentionBanner from "./index";
4
+ import { TypeCartRetentionBannerFields } from "./types";
5
+
6
+ import { act, fireEvent, render, screen } from "@testing-library/react";
7
+
8
+ // Mock dependencies
9
+ jest.mock("@shared/components/text", () => ({
10
+ Text: ({ children, ...props }: any) => <span {...props}>{children}</span>,
11
+ }));
12
+
13
+ jest.mock("@shared/contentful/blocks/button", () => ({
14
+ Button: ({ children, onClick, buttonLabel, ...props }: any) => (
15
+ <button onClick={onClick} data-testid="banner-button" {...props}>
16
+ {buttonLabel || children}
17
+ </button>
18
+ ),
19
+ }));
20
+
21
+ jest.mock("@shared/contentful/blocks/modal", () => ({
22
+ Modal: ({ isOpen, onRequestClose, children, ...props }: any) => (
23
+ <div data-testid="modal" data-open={isOpen} data-type={props.type}>
24
+ {isOpen && (
25
+ <>
26
+ <button data-testid="modal-close" onClick={onRequestClose}>
27
+ Close
28
+ </button>
29
+ {children}
30
+ </>
31
+ )}
32
+ </div>
33
+ ),
34
+ }));
35
+
36
+ jest.mock("@shared/hooks/contentful/use-contentful-rich-text", () => ({
37
+ renderContentfulRichText: (doc: any, _: any, className: string) => (
38
+ <div data-testid="rich-text" className={className}>
39
+ Rich Text Content
40
+ </div>
41
+ ),
42
+ }));
43
+
44
+ jest.mock("@shared/utils/contentful/to-document", () => ({
45
+ toDocument: (json: any) => json,
46
+ }));
47
+
48
+ describe("CartRetentionBanner", () => {
49
+ const defaultFields: TypeCartRetentionBannerFields = {
50
+ entryTitle: "Cart Banner",
51
+ anchorId: "cart-banner",
52
+ mainHeading: "Welcome back!",
53
+ description: { json: { content: [{ nodeType: "paragraph" }] } },
54
+ cta: { buttonLabel: "Continue checkout" },
55
+ };
56
+
57
+ beforeEach(() => {
58
+ jest.useFakeTimers();
59
+ });
60
+
61
+ afterEach(() => {
62
+ jest.useRealTimers();
63
+ });
64
+
65
+ describe("modal behavior", () => {
66
+ it("renders a section with anchorId", () => {
67
+ render(<CartRetentionBanner fields={defaultFields} />);
68
+ const section = document.querySelector("section#cart-banner");
69
+ expect(section).toBeInTheDocument();
70
+ });
71
+
72
+ it("renders with data-testid cart-retention-banner", () => {
73
+ render(<CartRetentionBanner fields={defaultFields} />);
74
+ expect(screen.getByTestId("cart-retention-banner")).toBeInTheDocument();
75
+ });
76
+
77
+ it("opens modal when checkShouldShowBanner returns true", async () => {
78
+ const checkShouldShowBanner = jest.fn().mockReturnValue(true);
79
+ render(
80
+ <CartRetentionBanner
81
+ fields={defaultFields}
82
+ checkShouldShowBanner={checkShouldShowBanner}
83
+ />
84
+ );
85
+
86
+ // Allow useEffect to fire
87
+ act(() => {
88
+ jest.advanceTimersByTime(200);
89
+ });
90
+
91
+ const modal = screen.getByTestId("modal");
92
+ expect(modal).toHaveAttribute("data-open", "true");
93
+ });
94
+
95
+ it("does not open modal when checkShouldShowBanner returns false", () => {
96
+ const checkShouldShowBanner = jest.fn().mockReturnValue(false);
97
+ render(
98
+ <CartRetentionBanner
99
+ fields={defaultFields}
100
+ checkShouldShowBanner={checkShouldShowBanner}
101
+ />
102
+ );
103
+
104
+ act(() => {
105
+ jest.advanceTimersByTime(200);
106
+ });
107
+
108
+ const modal = screen.getByTestId("modal");
109
+ expect(modal).toHaveAttribute("data-open", "false");
110
+ });
111
+
112
+ it("calls onBannerShown when modal opens", () => {
113
+ const onBannerShown = jest.fn();
114
+ const checkShouldShowBanner = jest.fn().mockReturnValue(true);
115
+ render(
116
+ <CartRetentionBanner
117
+ fields={defaultFields}
118
+ checkShouldShowBanner={checkShouldShowBanner}
119
+ onBannerShown={onBannerShown}
120
+ />
121
+ );
122
+
123
+ act(() => {
124
+ jest.advanceTimersByTime(200);
125
+ });
126
+
127
+ expect(onBannerShown).toHaveBeenCalled();
128
+ });
129
+
130
+ it("closes modal and calls onClose", () => {
131
+ const onClose = jest.fn();
132
+ const checkShouldShowBanner = jest.fn().mockReturnValue(true);
133
+ render(
134
+ <CartRetentionBanner
135
+ fields={defaultFields}
136
+ checkShouldShowBanner={checkShouldShowBanner}
137
+ onClose={onClose}
138
+ />
139
+ );
140
+
141
+ act(() => {
142
+ jest.advanceTimersByTime(200);
143
+ });
144
+
145
+ fireEvent.click(screen.getByTestId("modal-close"));
146
+ expect(onClose).toHaveBeenCalled();
147
+ });
148
+
149
+ it("calls onContinueCheckout when CTA button is clicked in modal", () => {
150
+ const onContinueCheckout = jest.fn();
151
+ const checkShouldShowBanner = jest.fn().mockReturnValue(true);
152
+ render(
153
+ <CartRetentionBanner
154
+ fields={defaultFields}
155
+ checkShouldShowBanner={checkShouldShowBanner}
156
+ onContinueCheckout={onContinueCheckout}
157
+ />
158
+ );
159
+
160
+ act(() => {
161
+ jest.advanceTimersByTime(200);
162
+ });
163
+
164
+ fireEvent.click(screen.getByTestId("banner-button"));
165
+ expect(onContinueCheckout).toHaveBeenCalled();
166
+ });
167
+
168
+ it("uses default heading when mainHeading is not provided", () => {
169
+ const checkShouldShowBanner = jest.fn().mockReturnValue(true);
170
+ const fields = { ...defaultFields, mainHeading: undefined };
171
+ render(
172
+ <CartRetentionBanner
173
+ fields={fields}
174
+ checkShouldShowBanner={checkShouldShowBanner}
175
+ />
176
+ );
177
+
178
+ act(() => {
179
+ jest.advanceTimersByTime(200);
180
+ });
181
+
182
+ expect(screen.getByText(/Welcome back/i)).toBeInTheDocument();
183
+ });
184
+
185
+ it("uses default CTA label when cta.buttonLabel is not provided", () => {
186
+ const checkShouldShowBanner = jest.fn().mockReturnValue(true);
187
+ const fields = { ...defaultFields, cta: {} };
188
+ render(
189
+ <CartRetentionBanner
190
+ fields={fields}
191
+ checkShouldShowBanner={checkShouldShowBanner}
192
+ />
193
+ );
194
+
195
+ act(() => {
196
+ jest.advanceTimersByTime(200);
197
+ });
198
+
199
+ expect(screen.getByTestId("banner-button")).toHaveTextContent(
200
+ "Continue checkout"
201
+ );
202
+ });
203
+
204
+ it("renders rich text description in modal", () => {
205
+ const checkShouldShowBanner = jest.fn().mockReturnValue(true);
206
+ render(
207
+ <CartRetentionBanner
208
+ fields={defaultFields}
209
+ checkShouldShowBanner={checkShouldShowBanner}
210
+ />
211
+ );
212
+
213
+ act(() => {
214
+ jest.advanceTimersByTime(200);
215
+ });
216
+
217
+ expect(screen.getByTestId("rich-text")).toBeInTheDocument();
218
+ });
219
+
220
+ it("does not render rich text when description is undefined", () => {
221
+ const checkShouldShowBanner = jest.fn().mockReturnValue(true);
222
+ const fields = { ...defaultFields, description: undefined };
223
+ render(
224
+ <CartRetentionBanner
225
+ fields={fields}
226
+ checkShouldShowBanner={checkShouldShowBanner}
227
+ />
228
+ );
229
+
230
+ act(() => {
231
+ jest.advanceTimersByTime(200);
232
+ });
233
+
234
+ expect(screen.queryByTestId("rich-text")).not.toBeInTheDocument();
235
+ });
236
+
237
+ it("passes type=cart-retention-banner to Modal", () => {
238
+ render(<CartRetentionBanner fields={defaultFields} />);
239
+ const modal = screen.getByTestId("modal");
240
+ expect(modal).toHaveAttribute("data-type", "cart-retention-banner");
241
+ });
242
+ });
243
+
244
+ describe("popup container mode", () => {
245
+ it("renders inline content when isInPopupContainer is true", () => {
246
+ render(
247
+ <CartRetentionBanner fields={defaultFields} isInPopupContainer={true} />
248
+ );
249
+ // Should render the pop-up-body div directly
250
+ const popupBody = document.querySelector(".pop-up-body");
251
+ expect(popupBody).toBeInTheDocument();
252
+ });
253
+
254
+ it("renders data-section-type attribute in popup mode", () => {
255
+ render(
256
+ <CartRetentionBanner fields={defaultFields} isInPopupContainer={true} />
257
+ );
258
+ const div = document.querySelector(
259
+ '[data-section-type="cart-retention-banner"]'
260
+ );
261
+ expect(div).toBeInTheDocument();
262
+ });
263
+
264
+ it("renders heading in popup mode", () => {
265
+ render(
266
+ <CartRetentionBanner fields={defaultFields} isInPopupContainer={true} />
267
+ );
268
+ expect(screen.getByText("Welcome back!")).toBeInTheDocument();
269
+ });
270
+
271
+ it("renders default heading in popup mode when mainHeading is undefined", () => {
272
+ const fields = { ...defaultFields, mainHeading: undefined };
273
+ render(<CartRetentionBanner fields={fields} isInPopupContainer={true} />);
274
+ expect(screen.getByText(/Welcome back/)).toBeInTheDocument();
275
+ });
276
+
277
+ it("renders CTA in popup mode", () => {
278
+ render(
279
+ <CartRetentionBanner fields={defaultFields} isInPopupContainer={true} />
280
+ );
281
+ expect(screen.getByTestId("banner-button")).toHaveTextContent(
282
+ "Continue checkout"
283
+ );
284
+ });
285
+
286
+ it("calls onContinueCheckout in popup mode", () => {
287
+ const onContinueCheckout = jest.fn();
288
+ render(
289
+ <CartRetentionBanner
290
+ fields={defaultFields}
291
+ isInPopupContainer={true}
292
+ onContinueCheckout={onContinueCheckout}
293
+ />
294
+ );
295
+ fireEvent.click(screen.getByTestId("banner-button"));
296
+ expect(onContinueCheckout).toHaveBeenCalled();
297
+ });
298
+
299
+ it("does not render Modal in popup mode", () => {
300
+ render(
301
+ <CartRetentionBanner fields={defaultFields} isInPopupContainer={true} />
302
+ );
303
+ expect(screen.queryByTestId("modal")).not.toBeInTheDocument();
304
+ });
305
+
306
+ it("renders rich text in popup mode", () => {
307
+ render(
308
+ <CartRetentionBanner fields={defaultFields} isInPopupContainer={true} />
309
+ );
310
+ expect(screen.getByTestId("rich-text")).toBeInTheDocument();
311
+ });
312
+ });
313
+
314
+ describe("timer cleanup", () => {
315
+ it("cleans up timeout on unmount", () => {
316
+ const checkShouldShowBanner = jest.fn().mockReturnValue(false);
317
+ const { unmount } = render(
318
+ <CartRetentionBanner
319
+ fields={defaultFields}
320
+ checkShouldShowBanner={checkShouldShowBanner}
321
+ />
322
+ );
323
+ unmount();
324
+ // No error should occur after unmount
325
+ act(() => {
326
+ jest.advanceTimersByTime(200);
327
+ });
328
+ });
329
+ });
330
+
331
+ describe("optional callback branches", () => {
332
+ it("handles close without onClose callback", () => {
333
+ const checkShouldShowBanner = jest.fn().mockReturnValue(true);
334
+ render(
335
+ <CartRetentionBanner
336
+ fields={defaultFields}
337
+ checkShouldShowBanner={checkShouldShowBanner}
338
+ />
339
+ );
340
+
341
+ act(() => {
342
+ jest.advanceTimersByTime(200);
343
+ });
344
+
345
+ // Should not throw when clicking close without onClose
346
+ fireEvent.click(screen.getByTestId("modal-close"));
347
+ expect(screen.getByTestId("modal")).toHaveAttribute("data-open", "false");
348
+ });
349
+
350
+ it("handles continue checkout without onContinueCheckout callback", () => {
351
+ const checkShouldShowBanner = jest.fn().mockReturnValue(true);
352
+ render(
353
+ <CartRetentionBanner
354
+ fields={defaultFields}
355
+ checkShouldShowBanner={checkShouldShowBanner}
356
+ />
357
+ );
358
+
359
+ act(() => {
360
+ jest.advanceTimersByTime(200);
361
+ });
362
+
363
+ // Should not throw when clicking CTA without onContinueCheckout
364
+ fireEvent.click(screen.getByTestId("banner-button"));
365
+ expect(screen.getByTestId("banner-button")).toBeInTheDocument();
366
+ });
367
+
368
+ it("handles continue checkout in popup mode without callback", () => {
369
+ render(
370
+ <CartRetentionBanner fields={defaultFields} isInPopupContainer={true} />
371
+ );
372
+ // Should not throw
373
+ fireEvent.click(screen.getByTestId("banner-button"));
374
+ expect(screen.getByTestId("banner-button")).toBeInTheDocument();
375
+ });
376
+
377
+ it("opens modal without onBannerShown callback", () => {
378
+ const checkShouldShowBanner = jest.fn().mockReturnValue(true);
379
+ render(
380
+ <CartRetentionBanner
381
+ fields={defaultFields}
382
+ checkShouldShowBanner={checkShouldShowBanner}
383
+ />
384
+ );
385
+
386
+ act(() => {
387
+ jest.advanceTimersByTime(200);
388
+ });
389
+
390
+ expect(screen.getByTestId("modal")).toHaveAttribute("data-open", "true");
391
+ });
392
+
393
+ it("does not open if checkShouldShowBanner is not provided", () => {
394
+ render(<CartRetentionBanner fields={defaultFields} />);
395
+
396
+ act(() => {
397
+ jest.advanceTimersByTime(200);
398
+ });
399
+
400
+ expect(screen.getByTestId("modal")).toHaveAttribute("data-open", "false");
401
+ });
402
+
403
+ it("renders popup without description", () => {
404
+ const fields = { ...defaultFields, description: undefined };
405
+ render(<CartRetentionBanner fields={fields} isInPopupContainer={true} />);
406
+ expect(screen.queryByTestId("rich-text")).not.toBeInTheDocument();
407
+ });
408
+ });
409
+ });