@windstream/react-shared-components 0.1.94 → 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 (297) hide show
  1. package/README.md +635 -635
  2. package/dist/contentful/index.esm.js +3 -3
  3. package/dist/contentful/index.esm.js.map +1 -1
  4. package/dist/contentful/index.js +3 -3
  5. package/dist/contentful/index.js.map +1 -1
  6. package/dist/core.d.ts +4 -4
  7. package/dist/index.esm.js +5 -13
  8. package/dist/index.esm.js.map +1 -1
  9. package/dist/index.js +5 -13
  10. package/dist/index.js.map +1 -1
  11. package/dist/next/index.esm.js +2 -2
  12. package/dist/next/index.esm.js.map +1 -1
  13. package/dist/next/index.js +2 -2
  14. package/dist/next/index.js.map +1 -1
  15. package/dist/styles.css +1 -1
  16. package/dist/utils/index.esm.js +1 -1
  17. package/dist/utils/index.esm.js.map +1 -1
  18. package/dist/utils/index.js +1 -1
  19. package/dist/utils/index.js.map +1 -1
  20. package/package.json +191 -191
  21. package/src/components/accordion/Accordion.stories.tsx +230 -230
  22. package/src/components/accordion/index.test.tsx +270 -270
  23. package/src/components/accordion/index.tsx +70 -70
  24. package/src/components/accordion/types.ts +12 -12
  25. package/src/components/alert-card/AlertCard.stories.tsx +171 -171
  26. package/src/components/alert-card/index.test.tsx +152 -152
  27. package/src/components/alert-card/index.tsx +41 -41
  28. package/src/components/alert-card/types.ts +13 -13
  29. package/src/components/animation-wrapper/index.test.tsx +424 -424
  30. package/src/components/animation-wrapper/index.tsx +129 -129
  31. package/src/components/animation-wrapper/types.ts +11 -11
  32. package/src/components/brand-button/BrandButton.stories.tsx +223 -223
  33. package/src/components/brand-button/helpers.ts +35 -35
  34. package/src/components/brand-button/index.test.tsx +292 -292
  35. package/src/components/brand-button/index.tsx +120 -120
  36. package/src/components/brand-button/types.ts +38 -38
  37. package/src/components/button/Button.stories.tsx +108 -108
  38. package/src/components/button/index.test.tsx +91 -91
  39. package/src/components/button/index.tsx +27 -27
  40. package/src/components/button/types.ts +14 -14
  41. package/src/components/call-button/CallButton.stories.tsx +324 -324
  42. package/src/components/call-button/index.test.tsx +260 -260
  43. package/src/components/call-button/index.tsx +106 -106
  44. package/src/components/call-button/types.ts +16 -16
  45. package/src/components/checkbox/Checkbox.stories.tsx +247 -247
  46. package/src/components/checkbox/index.test.tsx +252 -252
  47. package/src/components/checkbox/index.tsx +197 -197
  48. package/src/components/checkbox/types.ts +27 -27
  49. package/src/components/checklist/Checklist.stories.tsx +150 -150
  50. package/src/components/checklist/index.test.tsx +231 -231
  51. package/src/components/checklist/index.tsx +96 -96
  52. package/src/components/checklist/types.ts +23 -23
  53. package/src/components/collapse/Collapse.stories.tsx +255 -255
  54. package/src/components/collapse/index.test.tsx +277 -277
  55. package/src/components/collapse/index.tsx +47 -47
  56. package/src/components/collapse/types.ts +6 -6
  57. package/src/components/divider/Divider.stories.tsx +205 -205
  58. package/src/components/divider/index.test.tsx +53 -53
  59. package/src/components/divider/index.tsx +22 -22
  60. package/src/components/divider/type.ts +3 -3
  61. package/src/components/image/Image.stories.tsx +113 -113
  62. package/src/components/image/index.test.tsx +174 -174
  63. package/src/components/image/index.tsx +25 -25
  64. package/src/components/image/types.ts +40 -40
  65. package/src/components/input/Input.stories.tsx +325 -325
  66. package/src/components/input/index.test.tsx +348 -348
  67. package/src/components/input/index.tsx +177 -177
  68. package/src/components/input/types.ts +37 -37
  69. package/src/components/link/Link.stories.tsx +163 -163
  70. package/src/components/link/index.test.tsx +199 -199
  71. package/src/components/link/index.tsx +116 -116
  72. package/src/components/link/types.ts +25 -25
  73. package/src/components/list/List.stories.tsx +272 -272
  74. package/src/components/list/index.test.tsx +166 -166
  75. package/src/components/list/index.tsx +88 -88
  76. package/src/components/list/list-item/index.tsx +38 -38
  77. package/src/components/list/list-item/types.ts +13 -13
  78. package/src/components/list/types.ts +29 -29
  79. package/src/components/material-icon/MaterialIcon.stories.tsx +322 -322
  80. package/src/components/material-icon/constants.ts +99 -99
  81. package/src/components/material-icon/index.test.tsx +130 -130
  82. package/src/components/material-icon/index.tsx +47 -47
  83. package/src/components/material-icon/types.ts +31 -31
  84. package/src/components/modal/Modal.stories.tsx +171 -171
  85. package/src/components/modal/index.test.tsx +310 -310
  86. package/src/components/modal/index.tsx +164 -164
  87. package/src/components/modal/types.ts +24 -24
  88. package/src/components/next-image/index.test.tsx +406 -406
  89. package/src/components/next-image/index.tsx +74 -74
  90. package/src/components/next-image/types.ts +1 -1
  91. package/src/components/pagination/index.test.tsx +521 -521
  92. package/src/components/pagination/index.tsx +91 -91
  93. package/src/components/pagination/types.ts +6 -6
  94. package/src/components/radio-button/RadioButton.stories.tsx +307 -307
  95. package/src/components/radio-button/index.test.tsx +151 -151
  96. package/src/components/radio-button/index.tsx +75 -75
  97. package/src/components/radio-button/types.ts +21 -21
  98. package/src/components/see-more/SeeMore.stories.tsx +181 -181
  99. package/src/components/see-more/index.test.tsx +96 -96
  100. package/src/components/see-more/index.tsx +44 -44
  101. package/src/components/see-more/types.ts +4 -4
  102. package/src/components/select/Select.stories.tsx +411 -411
  103. package/src/components/select/index.test.tsx +256 -256
  104. package/src/components/select/index.tsx +155 -155
  105. package/src/components/select/types.ts +36 -36
  106. package/src/components/select-plan-button/SelectPlanButton.stories.tsx +184 -184
  107. package/src/components/select-plan-button/index.test.tsx +173 -173
  108. package/src/components/select-plan-button/index.tsx +63 -63
  109. package/src/components/select-plan-button/types.ts +17 -17
  110. package/src/components/skeleton/Skeleton.stories.tsx +179 -179
  111. package/src/components/skeleton/index.test.tsx +74 -74
  112. package/src/components/skeleton/index.tsx +61 -61
  113. package/src/components/skeleton/types.ts +4 -4
  114. package/src/components/spinner/Spinner.stories.tsx +335 -335
  115. package/src/components/spinner/index.test.tsx +76 -76
  116. package/src/components/spinner/index.tsx +44 -44
  117. package/src/components/spinner/types.ts +5 -5
  118. package/src/components/text/Text.stories.tsx +321 -321
  119. package/src/components/text/index.test.tsx +65 -65
  120. package/src/components/text/index.tsx +25 -25
  121. package/src/components/text/types.ts +45 -45
  122. package/src/components/tooltip/Tooltip.stories.tsx +219 -219
  123. package/src/components/tooltip/index.test.tsx +50 -50
  124. package/src/components/tooltip/index.tsx +74 -74
  125. package/src/components/tooltip/types.ts +7 -7
  126. package/src/components/view-cart-button/ViewCartButton.stories.tsx +252 -252
  127. package/src/components/view-cart-button/index.test.tsx +57 -57
  128. package/src/components/view-cart-button/index.tsx +42 -42
  129. package/src/components/view-cart-button/types.ts +5 -5
  130. package/src/contentful/blocks/accordion/Accordion.stories.mocks.tsx +128 -128
  131. package/src/contentful/blocks/accordion/Accordion.stories.tsx +98 -98
  132. package/src/contentful/blocks/accordion/index.test.tsx +218 -218
  133. package/src/contentful/blocks/accordion/index.tsx +114 -114
  134. package/src/contentful/blocks/accordion/types.ts +34 -34
  135. package/src/contentful/blocks/address-input-banner/index.test.tsx +132 -132
  136. package/src/contentful/blocks/address-input-banner/index.tsx +52 -52
  137. package/src/contentful/blocks/address-input-banner/types.ts +14 -14
  138. package/src/contentful/blocks/anchored-bottom-banner/index.test.tsx +287 -287
  139. package/src/contentful/blocks/anchored-bottom-banner/index.tsx +181 -181
  140. package/src/contentful/blocks/anchored-bottom-banner/types.ts +13 -13
  141. package/src/contentful/blocks/blogs-grid/BlogGrid.stories.mocks.tsx +144 -144
  142. package/src/contentful/blocks/blogs-grid/BlogGrid.stories.tsx +157 -157
  143. package/src/contentful/blocks/blogs-grid/index.test.tsx +355 -355
  144. package/src/contentful/blocks/blogs-grid/index.tsx +134 -134
  145. package/src/contentful/blocks/blogs-grid/types.ts +26 -26
  146. package/src/contentful/blocks/blogs-grid-base/index.test.tsx +274 -274
  147. package/src/contentful/blocks/blogs-grid-base/index.tsx +119 -119
  148. package/src/contentful/blocks/blogs-grid-base/types.ts +36 -36
  149. package/src/contentful/blocks/breadcrumbs/BreadcrumbNavigation.stories.tsx +147 -147
  150. package/src/contentful/blocks/breadcrumbs/index.test.tsx +281 -281
  151. package/src/contentful/blocks/breadcrumbs/index.tsx +95 -95
  152. package/src/contentful/blocks/breadcrumbs/types.ts +8 -8
  153. package/src/contentful/blocks/button/Button.stories.tsx +40 -40
  154. package/src/contentful/blocks/button/index.test.tsx +339 -339
  155. package/src/contentful/blocks/button/index.tsx +131 -131
  156. package/src/contentful/blocks/button/types.ts +39 -39
  157. package/src/contentful/blocks/callout/Callout.stories.tsx +23 -23
  158. package/src/contentful/blocks/callout/index.test.tsx +539 -539
  159. package/src/contentful/blocks/callout/index.tsx +277 -277
  160. package/src/contentful/blocks/callout/types.ts +78 -78
  161. package/src/contentful/blocks/cards/Cards.stories.tsx +23 -23
  162. package/src/contentful/blocks/cards/blog-card/index.test.tsx +218 -218
  163. package/src/contentful/blocks/cards/blog-card/index.tsx +129 -129
  164. package/src/contentful/blocks/cards/blog-card/types.ts +34 -34
  165. package/src/contentful/blocks/cards/floating-image-card/index.test.tsx +201 -201
  166. package/src/contentful/blocks/cards/floating-image-card/index.tsx +119 -119
  167. package/src/contentful/blocks/cards/floating-image-card/types.ts +30 -30
  168. package/src/contentful/blocks/cards/full-image-card/index.test.tsx +216 -216
  169. package/src/contentful/blocks/cards/full-image-card/index.tsx +130 -130
  170. package/src/contentful/blocks/cards/full-image-card/types.ts +29 -29
  171. package/src/contentful/blocks/cards/index.test.tsx +39 -39
  172. package/src/contentful/blocks/cards/index.tsx +13 -13
  173. package/src/contentful/blocks/cards/product-card/index.test.tsx +263 -263
  174. package/src/contentful/blocks/cards/product-card/index.tsx +251 -251
  175. package/src/contentful/blocks/cards/product-card/types.ts +28 -28
  176. package/src/contentful/blocks/cards/simple-card/index.test.tsx +364 -364
  177. package/src/contentful/blocks/cards/simple-card/index.tsx +325 -325
  178. package/src/contentful/blocks/cards/simple-card/types.ts +71 -71
  179. package/src/contentful/blocks/cards/testimonial-card/index.test.tsx +180 -180
  180. package/src/contentful/blocks/cards/testimonial-card/index.tsx +90 -90
  181. package/src/contentful/blocks/cards/testimonial-card/types.tsx +12 -12
  182. package/src/contentful/blocks/cards/types.ts +1 -1
  183. package/src/contentful/blocks/carousel/Carousel.stories.tsx +23 -23
  184. package/src/contentful/blocks/carousel/helper.test.tsx +539 -539
  185. package/src/contentful/blocks/carousel/helper.tsx +494 -494
  186. package/src/contentful/blocks/carousel/index.test.tsx +308 -308
  187. package/src/contentful/blocks/carousel/index.tsx +87 -87
  188. package/src/contentful/blocks/carousel/types.test.ts +16 -16
  189. package/src/contentful/blocks/carousel/types.ts +145 -145
  190. package/src/contentful/blocks/cart-retention-banner/index.test.tsx +409 -409
  191. package/src/contentful/blocks/cart-retention-banner/index.tsx +109 -109
  192. package/src/contentful/blocks/cart-retention-banner/types.ts +11 -11
  193. package/src/contentful/blocks/comparison-table/index.test.tsx +114 -114
  194. package/src/contentful/blocks/comparison-table/index.tsx +29 -29
  195. package/src/contentful/blocks/comparison-table/types.ts +6 -6
  196. package/src/contentful/blocks/cookiebanner/index.test.tsx +277 -277
  197. package/src/contentful/blocks/cookiebanner/index.tsx +146 -146
  198. package/src/contentful/blocks/cookiebanner/type.ts +7 -7
  199. package/src/contentful/blocks/cta-callout/CtaCallout.stories.tsx +46 -46
  200. package/src/contentful/blocks/cta-callout/index.test.tsx +244 -244
  201. package/src/contentful/blocks/cta-callout/index.tsx +73 -73
  202. package/src/contentful/blocks/cta-callout/types.ts +26 -26
  203. package/src/contentful/blocks/dynamic-tabs/index.test.tsx +240 -240
  204. package/src/contentful/blocks/dynamic-tabs/index.tsx +204 -204
  205. package/src/contentful/blocks/dynamic-tabs/types.ts +21 -21
  206. package/src/contentful/blocks/email-input-block/index.test.tsx +213 -213
  207. package/src/contentful/blocks/email-input-block/index.tsx +121 -121
  208. package/src/contentful/blocks/email-input-block/types.ts +16 -16
  209. package/src/contentful/blocks/find-kinetic/FindKinetic.stories.tsx +23 -23
  210. package/src/contentful/blocks/find-kinetic/index.test.tsx +269 -269
  211. package/src/contentful/blocks/find-kinetic/index.tsx +138 -138
  212. package/src/contentful/blocks/find-kinetic/types.ts +20 -20
  213. package/src/contentful/blocks/floating-banner/FloatingBanner.stories.tsx +34 -34
  214. package/src/contentful/blocks/floating-banner/index.test.tsx +246 -246
  215. package/src/contentful/blocks/floating-banner/index.tsx +97 -97
  216. package/src/contentful/blocks/floating-banner/types.ts +22 -22
  217. package/src/contentful/blocks/footer/Footer.stories.tsx +317 -317
  218. package/src/contentful/blocks/footer/index.test.tsx +302 -302
  219. package/src/contentful/blocks/footer/index.tsx +91 -91
  220. package/src/contentful/blocks/footer/types.ts +13 -13
  221. package/src/contentful/blocks/image-promo-bar/ImagePromoBar.stories.tsx +23 -23
  222. package/src/contentful/blocks/image-promo-bar/helper.test.tsx +61 -61
  223. package/src/contentful/blocks/image-promo-bar/helper.tsx +28 -28
  224. package/src/contentful/blocks/image-promo-bar/index.test.tsx +467 -467
  225. package/src/contentful/blocks/image-promo-bar/index.tsx +8 -6
  226. package/src/contentful/blocks/image-promo-bar/types.ts +44 -44
  227. package/src/contentful/blocks/image-promo-bar/vimeo-embed.test.tsx +142 -142
  228. package/src/contentful/blocks/image-promo-bar/vimeo-embed.tsx +93 -93
  229. package/src/contentful/blocks/image-promo-bar/youtube-embed.test.tsx +104 -104
  230. package/src/contentful/blocks/image-promo-bar/youtube-embed.tsx +46 -46
  231. package/src/contentful/blocks/modal/constants.ts +53 -53
  232. package/src/contentful/blocks/modal/index.test.tsx +209 -209
  233. package/src/contentful/blocks/modal/index.tsx +108 -108
  234. package/src/contentful/blocks/modal/types.ts +12 -12
  235. package/src/contentful/blocks/navigation/Navigation.stories.mocks.tsx +78 -78
  236. package/src/contentful/blocks/navigation/Navigation.stories.tsx +138 -138
  237. package/src/contentful/blocks/navigation/desktop-link-groups.tsx/index.test.tsx +208 -208
  238. package/src/contentful/blocks/navigation/desktop-link-groups.tsx/index.tsx +141 -141
  239. package/src/contentful/blocks/navigation/index.test.tsx +924 -924
  240. package/src/contentful/blocks/navigation/index.tsx +569 -569
  241. package/src/contentful/blocks/navigation/mobile-link-groups.tsx/index.test.tsx +131 -131
  242. package/src/contentful/blocks/navigation/mobile-link-groups.tsx/index.tsx +82 -82
  243. package/src/contentful/blocks/navigation/types.ts +71 -71
  244. package/src/contentful/blocks/primary-hero/PrimaryHero.stories.tsx +23 -23
  245. package/src/contentful/blocks/primary-hero/index.test.tsx +286 -286
  246. package/src/contentful/blocks/primary-hero/index.tsx +239 -239
  247. package/src/contentful/blocks/primary-hero/types.ts +37 -37
  248. package/src/contentful/blocks/search-block/index.test.tsx +268 -268
  249. package/src/contentful/blocks/search-block/index.tsx +90 -90
  250. package/src/contentful/blocks/search-block/types.ts +15 -15
  251. package/src/contentful/blocks/shape-background-wrapper/ShapeBackgroundWrapper.stories.tsx +26 -26
  252. package/src/contentful/blocks/shape-background-wrapper/index.test.tsx +284 -284
  253. package/src/contentful/blocks/shape-background-wrapper/index.tsx +124 -124
  254. package/src/contentful/blocks/shape-background-wrapper/types.ts +36 -36
  255. package/src/contentful/blocks/text/Text.stories.tsx +23 -23
  256. package/src/contentful/blocks/text/index.test.tsx +36 -36
  257. package/src/contentful/blocks/text/index.tsx +12 -12
  258. package/src/contentful/blocks/text/types.ts +1 -1
  259. package/src/contentful/index.test.ts +45 -45
  260. package/src/contentful/index.ts +105 -105
  261. package/src/global-mocks/contentful/to-document.ts +25 -25
  262. package/src/global-mocks/cookie.ts +48 -48
  263. package/src/global-mocks/cx.ts +37 -37
  264. package/src/global-mocks/index.ts +89 -89
  265. package/src/global-mocks/speed-card-bg.ts +27 -27
  266. package/src/global-mocks/utm.ts +49 -49
  267. package/src/hooks/contentful/use-contentful-rich-text.test.tsx +1758 -1758
  268. package/src/hooks/contentful/use-contentful-rich-text.tsx +309 -309
  269. package/src/hooks/contentful/use-processed-check-list.test.tsx +277 -277
  270. package/src/hooks/contentful/use-processed-check-list.ts +63 -63
  271. package/src/hooks/use-body-scroll-lock.test.ts +134 -134
  272. package/src/hooks/use-body-scroll-lock.ts +34 -34
  273. package/src/hooks/use-carousel-swipe.test.ts +393 -393
  274. package/src/hooks/use-carousel-swipe.ts +264 -264
  275. package/src/hooks/use-outside-click.test.ts +142 -142
  276. package/src/hooks/use-outside-click.ts +17 -17
  277. package/src/index.ts +107 -107
  278. package/src/next/index.test.ts +7 -7
  279. package/src/next/index.ts +5 -5
  280. package/src/setupTests.ts +52 -52
  281. package/src/stories/DocsTemplate.tsx +24 -24
  282. package/src/styles/globals.css +343 -343
  283. package/src/types/global.d.ts +9 -9
  284. package/src/types/micro-components.ts +99 -99
  285. package/src/types/utm.ts +49 -49
  286. package/src/utils/contentful/to-document.test.ts +85 -85
  287. package/src/utils/contentful/to-document.ts +24 -24
  288. package/src/utils/cookie.test.ts +180 -180
  289. package/src/utils/cookie.ts +84 -84
  290. package/src/utils/cx.test.ts +90 -90
  291. package/src/utils/cx.ts +49 -49
  292. package/src/utils/index.test.ts +115 -115
  293. package/src/utils/index.ts +41 -41
  294. package/src/utils/speed-card-bg.test.ts +46 -46
  295. package/src/utils/speed-card-bg.ts +24 -24
  296. package/src/utils/utm.test.ts +359 -359
  297. package/src/utils/utm.ts +221 -221
@@ -1,302 +1,302 @@
1
- import React from "react";
2
- import { Footer } from "./index";
3
- import { FooterProps } from "./types";
4
-
5
- import { fireEvent, render, screen } from "@testing-library/react";
6
-
7
- // Mock dependencies
8
- jest.mock("@shared/components/divider", () => ({
9
- Divider: ({ className }: { className?: string }) => (
10
- <hr data-testid="divider" className={className} />
11
- ),
12
- }));
13
-
14
- jest.mock("@shared/components/text", () => ({
15
- Text: ({ as: Tag = "span", children, className }: any) => (
16
- <Tag className={className}>{children}</Tag>
17
- ),
18
- }));
19
-
20
- jest.mock("../button", () => ({
21
- Button: ({
22
- onClick,
23
- linkClassName,
24
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
25
- linkVariant,
26
- buttonLabel,
27
- ...rest
28
- }: any) => (
29
- <button
30
- data-testid="footer-button"
31
- className={linkClassName}
32
- onClick={onClick}
33
- >
34
- {buttonLabel || rest.children || "Link"}
35
- </button>
36
- ),
37
- }));
38
-
39
- const mockLinks = [
40
- {
41
- title: "Products",
42
- items: {
43
- items: [
44
- { buttonLabel: "Internet", href: "/internet" },
45
- { buttonLabel: "TV", href: "/tv" },
46
- { buttonLabel: "Phone", href: "/phone" },
47
- ],
48
- },
49
- },
50
- {
51
- title: "Support",
52
- items: {
53
- items: [
54
- { buttonLabel: "Help Center", href: "/help" },
55
- { buttonLabel: "Contact Us", href: "/contact" },
56
- ],
57
- },
58
- },
59
- ];
60
-
61
- const mockBottomLinks = [
62
- { buttonLabel: "Privacy Policy", href: "/privacy" },
63
- { buttonLabel: "Terms of Use", href: "/terms" },
64
- { buttonLabel: "Accessibility", href: "/accessibility" },
65
- ];
66
-
67
- const defaultProps: FooterProps = {
68
- body: <p>Promotional content and disclaimers go here.</p>,
69
- links: mockLinks,
70
- bottomLinks: mockBottomLinks,
71
- copyrights: "All rights reserved.",
72
- terms: "Kinetic by Windstream.",
73
- maxWidth: true,
74
- };
75
-
76
- describe("Footer", () => {
77
- describe("Rendering", () => {
78
- it("renders with all props", () => {
79
- render(<Footer {...defaultProps} />);
80
- expect(
81
- screen.getByText("Promotional content and disclaimers go here.")
82
- ).toBeInTheDocument();
83
- expect(screen.getByText("Products")).toBeInTheDocument();
84
- expect(screen.getByText("Support")).toBeInTheDocument();
85
- expect(screen.getByText("All rights reserved.")).toBeInTheDocument();
86
- });
87
-
88
- it("renders with minimal props (body only)", () => {
89
- render(<Footer body={<span>Minimal body</span>} />);
90
- expect(screen.getByText("Minimal body")).toBeInTheDocument();
91
- });
92
-
93
- it("renders body content in aside with proper aria-label", () => {
94
- render(<Footer {...defaultProps} />);
95
- const aside = screen.getByLabelText("Promotions and disclaimers");
96
- expect(aside.tagName).toBe("ASIDE");
97
- expect(aside).toHaveClass("break-words");
98
- });
99
- });
100
-
101
- describe("Promotional section (body)", () => {
102
- it("renders ReactNode body content", () => {
103
- render(
104
- <Footer
105
- body={
106
- <div data-testid="promo">
107
- <strong>Big Sale!</strong> Save 50%
108
- </div>
109
- }
110
- />
111
- );
112
- expect(screen.getByTestId("promo")).toBeInTheDocument();
113
- expect(screen.getByText("Save 50%")).toBeInTheDocument();
114
- });
115
-
116
- it("renders plain text body", () => {
117
- render(<Footer body="Simple promotional text" />);
118
- expect(screen.getByText("Simple promotional text")).toBeInTheDocument();
119
- });
120
- });
121
-
122
- describe("Footer link groups", () => {
123
- it("renders link group titles", () => {
124
- render(<Footer {...defaultProps} />);
125
- expect(screen.getByText("Products")).toBeInTheDocument();
126
- expect(screen.getByText("Support")).toBeInTheDocument();
127
- });
128
-
129
- it("renders all link items within groups", () => {
130
- render(<Footer {...defaultProps} />);
131
- const buttons = screen.getAllByTestId("footer-button");
132
- // 3 Products links + 2 Support links + 3 bottom links = 8
133
- expect(buttons).toHaveLength(8);
134
- });
135
-
136
- it("renders nav elements with proper aria-labelledby", () => {
137
- const { container } = render(<Footer {...defaultProps} />);
138
- const navs = container.querySelectorAll("nav.site-links-group");
139
- expect(navs).toHaveLength(2);
140
- expect(navs[0]).toHaveAttribute("aria-labelledby", "footer-Products");
141
- expect(navs[1]).toHaveAttribute("aria-labelledby", "footer-Support");
142
- });
143
-
144
- it("renders no link groups when links is undefined", () => {
145
- render(<Footer body="No links" />);
146
- expect(screen.queryByText("Products")).not.toBeInTheDocument();
147
- });
148
-
149
- it("renders no link groups when links is empty", () => {
150
- render(<Footer body="No links" links={[]} />);
151
- expect(screen.queryByText("Products")).not.toBeInTheDocument();
152
- });
153
-
154
- it("handles link groups with missing title and items gracefully", () => {
155
- const partialLinks = [
156
- { title: undefined, items: undefined },
157
- { title: "Valid", items: { items: undefined } },
158
- { title: "Also Valid", items: { items: [] } },
159
- ];
160
- const { container } = render(
161
- <Footer body="Partial" links={partialLinks} />
162
- );
163
- const navs = container.querySelectorAll("nav.site-links-group");
164
- expect(navs).toHaveLength(3);
165
- expect(screen.getByText("Valid")).toBeInTheDocument();
166
- expect(screen.getByText("Also Valid")).toBeInTheDocument();
167
- });
168
-
169
- it("renders dividers between link groups on mobile", () => {
170
- render(<Footer {...defaultProps} />);
171
- const dividers = screen.getAllByTestId("divider");
172
- // 1 main divider + 2 mobile dividers (one per link group) + 1 bottom divider = 4
173
- expect(dividers.length).toBeGreaterThanOrEqual(3);
174
- });
175
- });
176
-
177
- describe("Footer layout", () => {
178
- it("renders with 4-column grid layout", () => {
179
- const { container } = render(<Footer {...defaultProps} />);
180
- const grid = container.querySelector(".lg\\:grid-cols-4");
181
- expect(grid).toBeInTheDocument();
182
- });
183
-
184
- it("applies inverse background and white text", () => {
185
- const { container } = render(<Footer {...defaultProps} />);
186
- expect(container.firstChild).toHaveClass("bg-bg-inverse");
187
- expect(container.firstChild).toHaveClass("text-white");
188
- });
189
- });
190
-
191
- describe("Max width", () => {
192
- it("applies max-w-120 when maxWidth is true (default)", () => {
193
- const { container } = render(<Footer {...defaultProps} />);
194
- const wrapper = container.querySelector(".max-w-120");
195
- expect(wrapper).toBeInTheDocument();
196
- });
197
-
198
- it("does not apply max-w-120 when maxWidth is false", () => {
199
- const { container } = render(
200
- <Footer {...defaultProps} maxWidth={false} />
201
- );
202
- const wrapper = container.querySelector(".max-w-120");
203
- expect(wrapper).not.toBeInTheDocument();
204
- });
205
- });
206
-
207
- describe("Copyright and terms", () => {
208
- it("renders copyright year with current year", () => {
209
- render(<Footer {...defaultProps} />);
210
- const currentYear = new Date().getFullYear().toString();
211
- const termsEl = screen.getByText(
212
- content =>
213
- content.includes(currentYear) &&
214
- content.includes("Kinetic by Windstream.")
215
- );
216
- expect(termsEl).toBeInTheDocument();
217
- });
218
-
219
- it("renders copyrights text", () => {
220
- render(<Footer {...defaultProps} />);
221
- expect(screen.getByText("All rights reserved.")).toBeInTheDocument();
222
- });
223
-
224
- it("renders terms with copyright symbol", () => {
225
- render(<Footer {...defaultProps} />);
226
- const termsEl = screen.getByText(/©.*Kinetic by Windstream/);
227
- expect(termsEl).toBeInTheDocument();
228
- expect(termsEl).toHaveClass("terms-text");
229
- });
230
-
231
- it("renders copyrights with whitespace-pre-line class", () => {
232
- render(<Footer {...defaultProps} />);
233
- const copyEl = screen.getByText("All rights reserved.");
234
- expect(copyEl).toHaveClass("copy-rights");
235
- expect(copyEl).toHaveClass("whitespace-pre-line");
236
- });
237
- });
238
-
239
- describe("Bottom links", () => {
240
- it("renders bottom links", () => {
241
- render(<Footer {...defaultProps} />);
242
- const legalNav = screen.getByLabelText("Footer legal");
243
- expect(legalNav).toBeInTheDocument();
244
- });
245
-
246
- it("renders correct number of bottom links", () => {
247
- render(<Footer {...defaultProps} />);
248
- const legalNav = screen.getByLabelText("Footer legal");
249
- const items = legalNav.querySelectorAll("li");
250
- expect(items).toHaveLength(3);
251
- });
252
-
253
- it("renders no bottom links when not provided", () => {
254
- render(<Footer body="No bottom" />);
255
- const legalNav = screen.getByLabelText("Footer legal");
256
- const items = legalNav.querySelectorAll("li");
257
- expect(items).toHaveLength(0);
258
- });
259
- });
260
-
261
- describe("onClick handler", () => {
262
- it("calls onClick when a site link is clicked", () => {
263
- const handleClick = jest.fn();
264
- render(<Footer {...defaultProps} onClick={handleClick} />);
265
- const buttons = screen.getAllByTestId("footer-button");
266
- fireEvent.click(buttons[0]);
267
- expect(handleClick).toHaveBeenCalledTimes(1);
268
- });
269
-
270
- it("calls onClick for each link clicked", () => {
271
- const handleClick = jest.fn();
272
- render(<Footer {...defaultProps} onClick={handleClick} />);
273
- const buttons = screen.getAllByTestId("footer-button");
274
- fireEvent.click(buttons[0]);
275
- fireEvent.click(buttons[1]);
276
- expect(handleClick).toHaveBeenCalledTimes(2);
277
- });
278
-
279
- it("does not error when onClick is not provided", () => {
280
- render(<Footer body="No click handler" links={mockLinks} />);
281
- const buttons = screen.getAllByTestId("footer-button");
282
- expect(() => fireEvent.click(buttons[0])).not.toThrow();
283
- });
284
- });
285
-
286
- describe("Dividers", () => {
287
- it("renders main divider between body and links", () => {
288
- render(<Footer {...defaultProps} />);
289
- const dividers = screen.getAllByTestId("divider");
290
- expect(dividers.length).toBeGreaterThanOrEqual(1);
291
- });
292
-
293
- it("renders dividers with inverse border class", () => {
294
- render(<Footer {...defaultProps} />);
295
- const dividers = screen.getAllByTestId("divider");
296
- const inverseDividers = dividers.filter(d =>
297
- d.className.includes("border-border-inverse")
298
- );
299
- expect(inverseDividers.length).toBeGreaterThanOrEqual(1);
300
- });
301
- });
302
- });
1
+ import React from "react";
2
+ import { Footer } from "./index";
3
+ import { FooterProps } from "./types";
4
+
5
+ import { fireEvent, render, screen } from "@testing-library/react";
6
+
7
+ // Mock dependencies
8
+ jest.mock("@shared/components/divider", () => ({
9
+ Divider: ({ className }: { className?: string }) => (
10
+ <hr data-testid="divider" className={className} />
11
+ ),
12
+ }));
13
+
14
+ jest.mock("@shared/components/text", () => ({
15
+ Text: ({ as: Tag = "span", children, className }: any) => (
16
+ <Tag className={className}>{children}</Tag>
17
+ ),
18
+ }));
19
+
20
+ jest.mock("../button", () => ({
21
+ Button: ({
22
+ onClick,
23
+ linkClassName,
24
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
25
+ linkVariant,
26
+ buttonLabel,
27
+ ...rest
28
+ }: any) => (
29
+ <button
30
+ data-testid="footer-button"
31
+ className={linkClassName}
32
+ onClick={onClick}
33
+ >
34
+ {buttonLabel || rest.children || "Link"}
35
+ </button>
36
+ ),
37
+ }));
38
+
39
+ const mockLinks = [
40
+ {
41
+ title: "Products",
42
+ items: {
43
+ items: [
44
+ { buttonLabel: "Internet", href: "/internet" },
45
+ { buttonLabel: "TV", href: "/tv" },
46
+ { buttonLabel: "Phone", href: "/phone" },
47
+ ],
48
+ },
49
+ },
50
+ {
51
+ title: "Support",
52
+ items: {
53
+ items: [
54
+ { buttonLabel: "Help Center", href: "/help" },
55
+ { buttonLabel: "Contact Us", href: "/contact" },
56
+ ],
57
+ },
58
+ },
59
+ ];
60
+
61
+ const mockBottomLinks = [
62
+ { buttonLabel: "Privacy Policy", href: "/privacy" },
63
+ { buttonLabel: "Terms of Use", href: "/terms" },
64
+ { buttonLabel: "Accessibility", href: "/accessibility" },
65
+ ];
66
+
67
+ const defaultProps: FooterProps = {
68
+ body: <p>Promotional content and disclaimers go here.</p>,
69
+ links: mockLinks,
70
+ bottomLinks: mockBottomLinks,
71
+ copyrights: "All rights reserved.",
72
+ terms: "Kinetic by Windstream.",
73
+ maxWidth: true,
74
+ };
75
+
76
+ describe("Footer", () => {
77
+ describe("Rendering", () => {
78
+ it("renders with all props", () => {
79
+ render(<Footer {...defaultProps} />);
80
+ expect(
81
+ screen.getByText("Promotional content and disclaimers go here.")
82
+ ).toBeInTheDocument();
83
+ expect(screen.getByText("Products")).toBeInTheDocument();
84
+ expect(screen.getByText("Support")).toBeInTheDocument();
85
+ expect(screen.getByText("All rights reserved.")).toBeInTheDocument();
86
+ });
87
+
88
+ it("renders with minimal props (body only)", () => {
89
+ render(<Footer body={<span>Minimal body</span>} />);
90
+ expect(screen.getByText("Minimal body")).toBeInTheDocument();
91
+ });
92
+
93
+ it("renders body content in aside with proper aria-label", () => {
94
+ render(<Footer {...defaultProps} />);
95
+ const aside = screen.getByLabelText("Promotions and disclaimers");
96
+ expect(aside.tagName).toBe("ASIDE");
97
+ expect(aside).toHaveClass("break-words");
98
+ });
99
+ });
100
+
101
+ describe("Promotional section (body)", () => {
102
+ it("renders ReactNode body content", () => {
103
+ render(
104
+ <Footer
105
+ body={
106
+ <div data-testid="promo">
107
+ <strong>Big Sale!</strong> Save 50%
108
+ </div>
109
+ }
110
+ />
111
+ );
112
+ expect(screen.getByTestId("promo")).toBeInTheDocument();
113
+ expect(screen.getByText("Save 50%")).toBeInTheDocument();
114
+ });
115
+
116
+ it("renders plain text body", () => {
117
+ render(<Footer body="Simple promotional text" />);
118
+ expect(screen.getByText("Simple promotional text")).toBeInTheDocument();
119
+ });
120
+ });
121
+
122
+ describe("Footer link groups", () => {
123
+ it("renders link group titles", () => {
124
+ render(<Footer {...defaultProps} />);
125
+ expect(screen.getByText("Products")).toBeInTheDocument();
126
+ expect(screen.getByText("Support")).toBeInTheDocument();
127
+ });
128
+
129
+ it("renders all link items within groups", () => {
130
+ render(<Footer {...defaultProps} />);
131
+ const buttons = screen.getAllByTestId("footer-button");
132
+ // 3 Products links + 2 Support links + 3 bottom links = 8
133
+ expect(buttons).toHaveLength(8);
134
+ });
135
+
136
+ it("renders nav elements with proper aria-labelledby", () => {
137
+ const { container } = render(<Footer {...defaultProps} />);
138
+ const navs = container.querySelectorAll("nav.site-links-group");
139
+ expect(navs).toHaveLength(2);
140
+ expect(navs[0]).toHaveAttribute("aria-labelledby", "footer-Products");
141
+ expect(navs[1]).toHaveAttribute("aria-labelledby", "footer-Support");
142
+ });
143
+
144
+ it("renders no link groups when links is undefined", () => {
145
+ render(<Footer body="No links" />);
146
+ expect(screen.queryByText("Products")).not.toBeInTheDocument();
147
+ });
148
+
149
+ it("renders no link groups when links is empty", () => {
150
+ render(<Footer body="No links" links={[]} />);
151
+ expect(screen.queryByText("Products")).not.toBeInTheDocument();
152
+ });
153
+
154
+ it("handles link groups with missing title and items gracefully", () => {
155
+ const partialLinks = [
156
+ { title: undefined, items: undefined },
157
+ { title: "Valid", items: { items: undefined } },
158
+ { title: "Also Valid", items: { items: [] } },
159
+ ];
160
+ const { container } = render(
161
+ <Footer body="Partial" links={partialLinks} />
162
+ );
163
+ const navs = container.querySelectorAll("nav.site-links-group");
164
+ expect(navs).toHaveLength(3);
165
+ expect(screen.getByText("Valid")).toBeInTheDocument();
166
+ expect(screen.getByText("Also Valid")).toBeInTheDocument();
167
+ });
168
+
169
+ it("renders dividers between link groups on mobile", () => {
170
+ render(<Footer {...defaultProps} />);
171
+ const dividers = screen.getAllByTestId("divider");
172
+ // 1 main divider + 2 mobile dividers (one per link group) + 1 bottom divider = 4
173
+ expect(dividers.length).toBeGreaterThanOrEqual(3);
174
+ });
175
+ });
176
+
177
+ describe("Footer layout", () => {
178
+ it("renders with 4-column grid layout", () => {
179
+ const { container } = render(<Footer {...defaultProps} />);
180
+ const grid = container.querySelector(".lg\\:grid-cols-4");
181
+ expect(grid).toBeInTheDocument();
182
+ });
183
+
184
+ it("applies inverse background and white text", () => {
185
+ const { container } = render(<Footer {...defaultProps} />);
186
+ expect(container.firstChild).toHaveClass("bg-bg-inverse");
187
+ expect(container.firstChild).toHaveClass("text-white");
188
+ });
189
+ });
190
+
191
+ describe("Max width", () => {
192
+ it("applies max-w-120 when maxWidth is true (default)", () => {
193
+ const { container } = render(<Footer {...defaultProps} />);
194
+ const wrapper = container.querySelector(".max-w-120");
195
+ expect(wrapper).toBeInTheDocument();
196
+ });
197
+
198
+ it("does not apply max-w-120 when maxWidth is false", () => {
199
+ const { container } = render(
200
+ <Footer {...defaultProps} maxWidth={false} />
201
+ );
202
+ const wrapper = container.querySelector(".max-w-120");
203
+ expect(wrapper).not.toBeInTheDocument();
204
+ });
205
+ });
206
+
207
+ describe("Copyright and terms", () => {
208
+ it("renders copyright year with current year", () => {
209
+ render(<Footer {...defaultProps} />);
210
+ const currentYear = new Date().getFullYear().toString();
211
+ const termsEl = screen.getByText(
212
+ content =>
213
+ content.includes(currentYear) &&
214
+ content.includes("Kinetic by Windstream.")
215
+ );
216
+ expect(termsEl).toBeInTheDocument();
217
+ });
218
+
219
+ it("renders copyrights text", () => {
220
+ render(<Footer {...defaultProps} />);
221
+ expect(screen.getByText("All rights reserved.")).toBeInTheDocument();
222
+ });
223
+
224
+ it("renders terms with copyright symbol", () => {
225
+ render(<Footer {...defaultProps} />);
226
+ const termsEl = screen.getByText(/©.*Kinetic by Windstream/);
227
+ expect(termsEl).toBeInTheDocument();
228
+ expect(termsEl).toHaveClass("terms-text");
229
+ });
230
+
231
+ it("renders copyrights with whitespace-pre-line class", () => {
232
+ render(<Footer {...defaultProps} />);
233
+ const copyEl = screen.getByText("All rights reserved.");
234
+ expect(copyEl).toHaveClass("copy-rights");
235
+ expect(copyEl).toHaveClass("whitespace-pre-line");
236
+ });
237
+ });
238
+
239
+ describe("Bottom links", () => {
240
+ it("renders bottom links", () => {
241
+ render(<Footer {...defaultProps} />);
242
+ const legalNav = screen.getByLabelText("Footer legal");
243
+ expect(legalNav).toBeInTheDocument();
244
+ });
245
+
246
+ it("renders correct number of bottom links", () => {
247
+ render(<Footer {...defaultProps} />);
248
+ const legalNav = screen.getByLabelText("Footer legal");
249
+ const items = legalNav.querySelectorAll("li");
250
+ expect(items).toHaveLength(3);
251
+ });
252
+
253
+ it("renders no bottom links when not provided", () => {
254
+ render(<Footer body="No bottom" />);
255
+ const legalNav = screen.getByLabelText("Footer legal");
256
+ const items = legalNav.querySelectorAll("li");
257
+ expect(items).toHaveLength(0);
258
+ });
259
+ });
260
+
261
+ describe("onClick handler", () => {
262
+ it("calls onClick when a site link is clicked", () => {
263
+ const handleClick = jest.fn();
264
+ render(<Footer {...defaultProps} onClick={handleClick} />);
265
+ const buttons = screen.getAllByTestId("footer-button");
266
+ fireEvent.click(buttons[0]);
267
+ expect(handleClick).toHaveBeenCalledTimes(1);
268
+ });
269
+
270
+ it("calls onClick for each link clicked", () => {
271
+ const handleClick = jest.fn();
272
+ render(<Footer {...defaultProps} onClick={handleClick} />);
273
+ const buttons = screen.getAllByTestId("footer-button");
274
+ fireEvent.click(buttons[0]);
275
+ fireEvent.click(buttons[1]);
276
+ expect(handleClick).toHaveBeenCalledTimes(2);
277
+ });
278
+
279
+ it("does not error when onClick is not provided", () => {
280
+ render(<Footer body="No click handler" links={mockLinks} />);
281
+ const buttons = screen.getAllByTestId("footer-button");
282
+ expect(() => fireEvent.click(buttons[0])).not.toThrow();
283
+ });
284
+ });
285
+
286
+ describe("Dividers", () => {
287
+ it("renders main divider between body and links", () => {
288
+ render(<Footer {...defaultProps} />);
289
+ const dividers = screen.getAllByTestId("divider");
290
+ expect(dividers.length).toBeGreaterThanOrEqual(1);
291
+ });
292
+
293
+ it("renders dividers with inverse border class", () => {
294
+ render(<Footer {...defaultProps} />);
295
+ const dividers = screen.getAllByTestId("divider");
296
+ const inverseDividers = dividers.filter(d =>
297
+ d.className.includes("border-border-inverse")
298
+ );
299
+ expect(inverseDividers.length).toBeGreaterThanOrEqual(1);
300
+ });
301
+ });
302
+ });