@windstream/react-shared-components 0.1.93 → 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 (298) 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 +6 -6
  7. package/dist/index.d.ts +2 -2
  8. package/dist/index.esm.js +13 -5
  9. package/dist/index.esm.js.map +1 -1
  10. package/dist/index.js +13 -5
  11. package/dist/index.js.map +1 -1
  12. package/dist/next/index.esm.js +2 -2
  13. package/dist/next/index.esm.js.map +1 -1
  14. package/dist/next/index.js +2 -2
  15. package/dist/next/index.js.map +1 -1
  16. package/dist/styles.css +1 -1
  17. package/dist/utils/index.esm.js +1 -1
  18. package/dist/utils/index.esm.js.map +1 -1
  19. package/dist/utils/index.js +1 -1
  20. package/dist/utils/index.js.map +1 -1
  21. package/package.json +191 -185
  22. package/src/components/accordion/Accordion.stories.tsx +230 -230
  23. package/src/components/accordion/index.test.tsx +270 -0
  24. package/src/components/accordion/index.tsx +70 -70
  25. package/src/components/accordion/types.ts +12 -12
  26. package/src/components/alert-card/AlertCard.stories.tsx +171 -171
  27. package/src/components/alert-card/index.test.tsx +152 -0
  28. package/src/components/alert-card/index.tsx +41 -41
  29. package/src/components/alert-card/types.ts +13 -13
  30. package/src/components/animation-wrapper/index.test.tsx +424 -0
  31. package/src/components/animation-wrapper/index.tsx +129 -129
  32. package/src/components/animation-wrapper/types.ts +11 -11
  33. package/src/components/brand-button/BrandButton.stories.tsx +223 -223
  34. package/src/components/brand-button/helpers.ts +35 -35
  35. package/src/components/brand-button/index.test.tsx +292 -0
  36. package/src/components/brand-button/index.tsx +120 -120
  37. package/src/components/brand-button/types.ts +38 -38
  38. package/src/components/button/Button.stories.tsx +108 -108
  39. package/src/components/button/index.test.tsx +91 -0
  40. package/src/components/button/index.tsx +27 -27
  41. package/src/components/button/types.ts +14 -14
  42. package/src/components/call-button/CallButton.stories.tsx +324 -324
  43. package/src/components/call-button/index.test.tsx +260 -0
  44. package/src/components/call-button/index.tsx +106 -106
  45. package/src/components/call-button/types.ts +16 -16
  46. package/src/components/checkbox/Checkbox.stories.tsx +247 -247
  47. package/src/components/checkbox/index.test.tsx +252 -0
  48. package/src/components/checkbox/index.tsx +197 -197
  49. package/src/components/checkbox/types.ts +27 -27
  50. package/src/components/checklist/Checklist.stories.tsx +150 -150
  51. package/src/components/checklist/index.test.tsx +231 -0
  52. package/src/components/checklist/index.tsx +96 -61
  53. package/src/components/checklist/types.ts +23 -17
  54. package/src/components/collapse/Collapse.stories.tsx +255 -255
  55. package/src/components/collapse/index.test.tsx +277 -0
  56. package/src/components/collapse/index.tsx +47 -46
  57. package/src/components/collapse/types.ts +6 -6
  58. package/src/components/divider/Divider.stories.tsx +205 -205
  59. package/src/components/divider/index.test.tsx +53 -0
  60. package/src/components/divider/index.tsx +22 -22
  61. package/src/components/divider/type.ts +3 -3
  62. package/src/components/image/Image.stories.tsx +113 -113
  63. package/src/components/image/index.test.tsx +174 -0
  64. package/src/components/image/index.tsx +25 -25
  65. package/src/components/image/types.ts +40 -40
  66. package/src/components/input/Input.stories.tsx +325 -325
  67. package/src/components/input/index.test.tsx +348 -0
  68. package/src/components/input/index.tsx +177 -177
  69. package/src/components/input/types.ts +37 -37
  70. package/src/components/link/Link.stories.tsx +163 -163
  71. package/src/components/link/index.test.tsx +199 -0
  72. package/src/components/link/index.tsx +116 -116
  73. package/src/components/link/types.ts +25 -25
  74. package/src/components/list/List.stories.tsx +272 -272
  75. package/src/components/list/index.test.tsx +166 -0
  76. package/src/components/list/index.tsx +88 -88
  77. package/src/components/list/list-item/index.tsx +38 -38
  78. package/src/components/list/list-item/types.ts +13 -13
  79. package/src/components/list/types.ts +29 -29
  80. package/src/components/material-icon/MaterialIcon.stories.tsx +322 -322
  81. package/src/components/material-icon/constants.ts +99 -99
  82. package/src/components/material-icon/index.test.tsx +130 -0
  83. package/src/components/material-icon/index.tsx +47 -47
  84. package/src/components/material-icon/types.ts +31 -31
  85. package/src/components/modal/Modal.stories.tsx +171 -171
  86. package/src/components/modal/index.test.tsx +310 -0
  87. package/src/components/modal/index.tsx +164 -164
  88. package/src/components/modal/types.ts +24 -24
  89. package/src/components/next-image/index.test.tsx +406 -0
  90. package/src/components/next-image/index.tsx +74 -74
  91. package/src/components/next-image/types.ts +1 -1
  92. package/src/components/pagination/index.test.tsx +521 -0
  93. package/src/components/pagination/index.tsx +91 -91
  94. package/src/components/pagination/types.ts +6 -6
  95. package/src/components/radio-button/RadioButton.stories.tsx +307 -307
  96. package/src/components/radio-button/index.test.tsx +151 -0
  97. package/src/components/radio-button/index.tsx +75 -75
  98. package/src/components/radio-button/types.ts +21 -21
  99. package/src/components/see-more/SeeMore.stories.tsx +181 -181
  100. package/src/components/see-more/index.test.tsx +96 -0
  101. package/src/components/see-more/index.tsx +44 -44
  102. package/src/components/see-more/types.ts +4 -4
  103. package/src/components/select/Select.stories.tsx +411 -411
  104. package/src/components/select/index.test.tsx +256 -0
  105. package/src/components/select/index.tsx +155 -155
  106. package/src/components/select/types.ts +36 -36
  107. package/src/components/select-plan-button/SelectPlanButton.stories.tsx +184 -184
  108. package/src/components/select-plan-button/index.test.tsx +173 -0
  109. package/src/components/select-plan-button/index.tsx +63 -63
  110. package/src/components/select-plan-button/types.ts +17 -17
  111. package/src/components/skeleton/Skeleton.stories.tsx +179 -179
  112. package/src/components/skeleton/index.test.tsx +74 -0
  113. package/src/components/skeleton/index.tsx +61 -61
  114. package/src/components/skeleton/types.ts +4 -4
  115. package/src/components/spinner/Spinner.stories.tsx +335 -335
  116. package/src/components/spinner/index.test.tsx +76 -0
  117. package/src/components/spinner/index.tsx +44 -44
  118. package/src/components/spinner/types.ts +5 -5
  119. package/src/components/text/Text.stories.tsx +321 -321
  120. package/src/components/text/index.test.tsx +65 -0
  121. package/src/components/text/index.tsx +25 -25
  122. package/src/components/text/types.ts +45 -45
  123. package/src/components/tooltip/Tooltip.stories.tsx +219 -219
  124. package/src/components/tooltip/index.test.tsx +50 -0
  125. package/src/components/tooltip/index.tsx +74 -74
  126. package/src/components/tooltip/types.ts +7 -7
  127. package/src/components/view-cart-button/ViewCartButton.stories.tsx +252 -252
  128. package/src/components/view-cart-button/index.test.tsx +57 -0
  129. package/src/components/view-cart-button/index.tsx +42 -42
  130. package/src/components/view-cart-button/types.ts +5 -5
  131. package/src/contentful/blocks/accordion/Accordion.stories.mocks.tsx +128 -128
  132. package/src/contentful/blocks/accordion/Accordion.stories.tsx +98 -98
  133. package/src/contentful/blocks/accordion/index.test.tsx +218 -0
  134. package/src/contentful/blocks/accordion/index.tsx +114 -112
  135. package/src/contentful/blocks/accordion/types.ts +34 -34
  136. package/src/contentful/blocks/address-input-banner/index.test.tsx +132 -0
  137. package/src/contentful/blocks/address-input-banner/index.tsx +52 -52
  138. package/src/contentful/blocks/address-input-banner/types.ts +14 -14
  139. package/src/contentful/blocks/anchored-bottom-banner/index.test.tsx +287 -0
  140. package/src/contentful/blocks/anchored-bottom-banner/index.tsx +181 -181
  141. package/src/contentful/blocks/anchored-bottom-banner/types.ts +13 -13
  142. package/src/contentful/blocks/blogs-grid/BlogGrid.stories.mocks.tsx +144 -144
  143. package/src/contentful/blocks/blogs-grid/BlogGrid.stories.tsx +157 -156
  144. package/src/contentful/blocks/blogs-grid/index.test.tsx +355 -0
  145. package/src/contentful/blocks/blogs-grid/index.tsx +134 -134
  146. package/src/contentful/blocks/blogs-grid/types.ts +26 -26
  147. package/src/contentful/blocks/blogs-grid-base/index.test.tsx +274 -0
  148. package/src/contentful/blocks/blogs-grid-base/index.tsx +119 -119
  149. package/src/contentful/blocks/blogs-grid-base/types.ts +36 -36
  150. package/src/contentful/blocks/breadcrumbs/BreadcrumbNavigation.stories.tsx +147 -147
  151. package/src/contentful/blocks/breadcrumbs/index.test.tsx +281 -0
  152. package/src/contentful/blocks/breadcrumbs/index.tsx +95 -95
  153. package/src/contentful/blocks/breadcrumbs/types.ts +8 -8
  154. package/src/contentful/blocks/button/Button.stories.tsx +40 -40
  155. package/src/contentful/blocks/button/index.test.tsx +339 -0
  156. package/src/contentful/blocks/button/index.tsx +131 -131
  157. package/src/contentful/blocks/button/types.ts +39 -39
  158. package/src/contentful/blocks/callout/Callout.stories.tsx +23 -23
  159. package/src/contentful/blocks/callout/index.test.tsx +539 -0
  160. package/src/contentful/blocks/callout/index.tsx +277 -277
  161. package/src/contentful/blocks/callout/types.ts +78 -78
  162. package/src/contentful/blocks/cards/Cards.stories.tsx +23 -23
  163. package/src/contentful/blocks/cards/blog-card/index.test.tsx +218 -0
  164. package/src/contentful/blocks/cards/blog-card/index.tsx +129 -129
  165. package/src/contentful/blocks/cards/blog-card/types.ts +34 -34
  166. package/src/contentful/blocks/cards/floating-image-card/index.test.tsx +201 -0
  167. package/src/contentful/blocks/cards/floating-image-card/index.tsx +119 -119
  168. package/src/contentful/blocks/cards/floating-image-card/types.ts +30 -30
  169. package/src/contentful/blocks/cards/full-image-card/index.test.tsx +216 -0
  170. package/src/contentful/blocks/cards/full-image-card/index.tsx +130 -130
  171. package/src/contentful/blocks/cards/full-image-card/types.ts +29 -29
  172. package/src/contentful/blocks/cards/index.test.tsx +39 -0
  173. package/src/contentful/blocks/cards/index.tsx +13 -13
  174. package/src/contentful/blocks/cards/product-card/index.test.tsx +263 -0
  175. package/src/contentful/blocks/cards/product-card/index.tsx +251 -251
  176. package/src/contentful/blocks/cards/product-card/types.ts +28 -28
  177. package/src/contentful/blocks/cards/simple-card/index.test.tsx +364 -0
  178. package/src/contentful/blocks/cards/simple-card/index.tsx +325 -325
  179. package/src/contentful/blocks/cards/simple-card/types.ts +71 -71
  180. package/src/contentful/blocks/cards/testimonial-card/index.test.tsx +180 -0
  181. package/src/contentful/blocks/cards/testimonial-card/index.tsx +90 -90
  182. package/src/contentful/blocks/cards/testimonial-card/types.tsx +12 -12
  183. package/src/contentful/blocks/cards/types.ts +1 -1
  184. package/src/contentful/blocks/carousel/Carousel.stories.tsx +23 -23
  185. package/src/contentful/blocks/carousel/helper.test.tsx +539 -0
  186. package/src/contentful/blocks/carousel/helper.tsx +494 -494
  187. package/src/contentful/blocks/carousel/index.test.tsx +308 -0
  188. package/src/contentful/blocks/carousel/index.tsx +87 -87
  189. package/src/contentful/blocks/carousel/types.test.ts +16 -0
  190. package/src/contentful/blocks/carousel/types.ts +145 -145
  191. package/src/contentful/blocks/cart-retention-banner/index.test.tsx +409 -0
  192. package/src/contentful/blocks/cart-retention-banner/index.tsx +109 -109
  193. package/src/contentful/blocks/cart-retention-banner/types.ts +11 -11
  194. package/src/contentful/blocks/comparison-table/index.test.tsx +114 -0
  195. package/src/contentful/blocks/comparison-table/index.tsx +29 -29
  196. package/src/contentful/blocks/comparison-table/types.ts +6 -6
  197. package/src/contentful/blocks/cookiebanner/index.test.tsx +277 -0
  198. package/src/contentful/blocks/cookiebanner/index.tsx +146 -146
  199. package/src/contentful/blocks/cookiebanner/type.ts +7 -7
  200. package/src/contentful/blocks/cta-callout/CtaCallout.stories.tsx +46 -46
  201. package/src/contentful/blocks/cta-callout/index.test.tsx +244 -0
  202. package/src/contentful/blocks/cta-callout/index.tsx +73 -73
  203. package/src/contentful/blocks/cta-callout/types.ts +26 -26
  204. package/src/contentful/blocks/dynamic-tabs/index.test.tsx +240 -0
  205. package/src/contentful/blocks/dynamic-tabs/index.tsx +204 -204
  206. package/src/contentful/blocks/dynamic-tabs/types.ts +21 -21
  207. package/src/contentful/blocks/email-input-block/index.test.tsx +213 -0
  208. package/src/contentful/blocks/email-input-block/index.tsx +121 -116
  209. package/src/contentful/blocks/email-input-block/types.ts +16 -16
  210. package/src/contentful/blocks/find-kinetic/FindKinetic.stories.tsx +23 -23
  211. package/src/contentful/blocks/find-kinetic/index.test.tsx +269 -0
  212. package/src/contentful/blocks/find-kinetic/index.tsx +138 -138
  213. package/src/contentful/blocks/find-kinetic/types.ts +20 -20
  214. package/src/contentful/blocks/floating-banner/FloatingBanner.stories.tsx +34 -34
  215. package/src/contentful/blocks/floating-banner/index.test.tsx +246 -0
  216. package/src/contentful/blocks/floating-banner/index.tsx +97 -97
  217. package/src/contentful/blocks/floating-banner/types.ts +22 -22
  218. package/src/contentful/blocks/footer/Footer.stories.tsx +317 -317
  219. package/src/contentful/blocks/footer/index.test.tsx +302 -0
  220. package/src/contentful/blocks/footer/index.tsx +91 -91
  221. package/src/contentful/blocks/footer/types.ts +13 -13
  222. package/src/contentful/blocks/image-promo-bar/ImagePromoBar.stories.tsx +23 -23
  223. package/src/contentful/blocks/image-promo-bar/helper.test.tsx +61 -0
  224. package/src/contentful/blocks/image-promo-bar/helper.tsx +28 -28
  225. package/src/contentful/blocks/image-promo-bar/index.test.tsx +467 -0
  226. package/src/contentful/blocks/image-promo-bar/index.tsx +246 -246
  227. package/src/contentful/blocks/image-promo-bar/types.ts +44 -44
  228. package/src/contentful/blocks/image-promo-bar/vimeo-embed.test.tsx +142 -0
  229. package/src/contentful/blocks/image-promo-bar/vimeo-embed.tsx +93 -93
  230. package/src/contentful/blocks/image-promo-bar/youtube-embed.test.tsx +104 -0
  231. package/src/contentful/blocks/image-promo-bar/youtube-embed.tsx +46 -46
  232. package/src/contentful/blocks/modal/constants.ts +53 -53
  233. package/src/contentful/blocks/modal/index.test.tsx +209 -0
  234. package/src/contentful/blocks/modal/index.tsx +108 -108
  235. package/src/contentful/blocks/modal/types.ts +12 -12
  236. package/src/contentful/blocks/navigation/Navigation.stories.mocks.tsx +78 -78
  237. package/src/contentful/blocks/navigation/Navigation.stories.tsx +138 -138
  238. package/src/contentful/blocks/navigation/desktop-link-groups.tsx/index.test.tsx +208 -0
  239. package/src/contentful/blocks/navigation/desktop-link-groups.tsx/index.tsx +141 -141
  240. package/src/contentful/blocks/navigation/index.test.tsx +924 -0
  241. package/src/contentful/blocks/navigation/index.tsx +569 -569
  242. package/src/contentful/blocks/navigation/mobile-link-groups.tsx/index.test.tsx +131 -0
  243. package/src/contentful/blocks/navigation/mobile-link-groups.tsx/index.tsx +82 -82
  244. package/src/contentful/blocks/navigation/types.ts +71 -71
  245. package/src/contentful/blocks/primary-hero/PrimaryHero.stories.tsx +23 -23
  246. package/src/contentful/blocks/primary-hero/index.test.tsx +286 -0
  247. package/src/contentful/blocks/primary-hero/index.tsx +239 -236
  248. package/src/contentful/blocks/primary-hero/types.ts +37 -37
  249. package/src/contentful/blocks/search-block/index.test.tsx +268 -0
  250. package/src/contentful/blocks/search-block/index.tsx +90 -90
  251. package/src/contentful/blocks/search-block/types.ts +15 -15
  252. package/src/contentful/blocks/shape-background-wrapper/ShapeBackgroundWrapper.stories.tsx +26 -26
  253. package/src/contentful/blocks/shape-background-wrapper/index.test.tsx +284 -0
  254. package/src/contentful/blocks/shape-background-wrapper/index.tsx +124 -124
  255. package/src/contentful/blocks/shape-background-wrapper/types.ts +36 -36
  256. package/src/contentful/blocks/text/Text.stories.tsx +23 -23
  257. package/src/contentful/blocks/text/index.test.tsx +36 -0
  258. package/src/contentful/blocks/text/index.tsx +12 -12
  259. package/src/contentful/blocks/text/types.ts +1 -1
  260. package/src/contentful/index.test.ts +45 -0
  261. package/src/contentful/index.ts +105 -105
  262. package/src/global-mocks/contentful/to-document.ts +25 -0
  263. package/src/global-mocks/cookie.ts +48 -0
  264. package/src/global-mocks/cx.ts +37 -0
  265. package/src/global-mocks/index.ts +89 -0
  266. package/src/global-mocks/speed-card-bg.ts +27 -0
  267. package/src/global-mocks/utm.ts +49 -0
  268. package/src/hooks/contentful/use-contentful-rich-text.test.tsx +1758 -0
  269. package/src/hooks/contentful/use-contentful-rich-text.tsx +309 -309
  270. package/src/hooks/contentful/use-processed-check-list.test.tsx +277 -0
  271. package/src/hooks/contentful/use-processed-check-list.ts +63 -63
  272. package/src/hooks/use-body-scroll-lock.test.ts +134 -0
  273. package/src/hooks/use-body-scroll-lock.ts +34 -34
  274. package/src/hooks/use-carousel-swipe.test.ts +393 -0
  275. package/src/hooks/use-carousel-swipe.ts +264 -264
  276. package/src/hooks/use-outside-click.test.ts +142 -0
  277. package/src/hooks/use-outside-click.ts +17 -17
  278. package/src/index.ts +107 -107
  279. package/src/next/index.test.ts +7 -0
  280. package/src/next/index.ts +5 -5
  281. package/src/setupTests.ts +52 -46
  282. package/src/stories/DocsTemplate.tsx +24 -24
  283. package/src/styles/globals.css +343 -343
  284. package/src/types/global.d.ts +9 -9
  285. package/src/types/micro-components.ts +99 -99
  286. package/src/types/utm.ts +49 -49
  287. package/src/utils/contentful/to-document.test.ts +85 -0
  288. package/src/utils/contentful/to-document.ts +24 -24
  289. package/src/utils/cookie.test.ts +180 -0
  290. package/src/utils/cookie.ts +84 -84
  291. package/src/utils/cx.test.ts +90 -0
  292. package/src/utils/cx.ts +49 -49
  293. package/src/utils/index.test.ts +115 -0
  294. package/src/utils/index.ts +41 -41
  295. package/src/utils/speed-card-bg.test.ts +46 -0
  296. package/src/utils/speed-card-bg.ts +24 -24
  297. package/src/utils/utm.test.ts +359 -0
  298. package/src/utils/utm.ts +221 -221
@@ -1,251 +1,251 @@
1
- "use client";
2
-
3
- import React, { useState } from "react";
4
- import { ProductCardProps } from "./types";
5
-
6
- import { Button } from "@shared/components/button";
7
- import { Checklist } from "@shared/components/checklist";
8
- import { Image } from "@shared/components/image";
9
- import { MaterialIcon } from "@shared/components/material-icon";
10
- import { SelectPlanButton } from "@shared/components/select-plan-button";
11
- import { Text } from "@shared/components/text";
12
- import { cx } from "@shared/utils";
13
-
14
- export const ProductCard: React.FC<ProductCardProps> = ({
15
- planName,
16
- planSubtext,
17
- speed,
18
- price,
19
- description,
20
- bestValue = false,
21
- bestValueText = "best value",
22
- giftBadge,
23
- innerBadge,
24
- theme = "light",
25
- featuresTitle = "Business Ready Internet features",
26
- features = [],
27
- isExpanded: controlledExpanded,
28
- onToggleExpand,
29
- onCtaClick,
30
- renderCheckPlans,
31
- cta,
32
- techType,
33
- ismaxSpeed,
34
- }) => {
35
- const [internalExpanded, setInternalExpanded] = useState(false);
36
- // Use parent state if provided, otherwise use internal state
37
- const isExpanded =
38
- controlledExpanded !== undefined ? controlledExpanded : internalExpanded;
39
-
40
- const handleToggle = () => {
41
- if (onToggleExpand) {
42
- onToggleExpand();
43
- } else {
44
- setInternalExpanded(!internalExpanded);
45
- }
46
- };
47
-
48
- const isDark = theme === "dark";
49
-
50
- return (
51
- <article
52
- className={cx(
53
- "relative flex h-full w-full max-w-[392px] flex-col",
54
- !bestValue && "md:pt-[38px]"
55
- )}
56
- >
57
- {/* Best Value Banner */}
58
- {bestValue && (
59
- <div className="label3 rounded-t-[20px] bg-bg-fill-brand-accent px-6 py-2 text-center text-text">
60
- {bestValueText}
61
- </div>
62
- )}
63
-
64
- {/* Main Card */}
65
- <div
66
- className={cx(
67
- "flex flex-grow flex-col gap-5 rounded-card px-5 py-7 shadow-cardDrop",
68
- isDark
69
- ? "text-inverse border-2 border-border-accent bg-bg-fill-inverse"
70
- : "bg-bg text-text",
71
- bestValue ? "rounded-t-none border-t-0" : ""
72
- )}
73
- >
74
- {/* Plan Name & Price */}
75
- <header className="overflow-none flex items-start justify-between gap-4">
76
- <div className="flex flex-col">
77
- <Text
78
- as="h3"
79
- className={cx(
80
- "subheading2",
81
- isDark ? "text-text-accent-on-bg-inverse" : "text-text-brand"
82
- )}
83
- >
84
- {planName}
85
- </Text>
86
- <Text
87
- as="p"
88
- className={`label3 text-wrap ${isDark ? "text-text-accent-on-bg-inverse" : "text-text-brand"}`}
89
- >
90
- {planSubtext}
91
- </Text>
92
- </div>
93
- <div className="flex items-start">
94
- <Text
95
- as="span"
96
- className={cx(
97
- "label2 mr-1 mt-[2px]",
98
- isDark ? "text-text-inverse" : "text-text"
99
- )}
100
- >
101
- $
102
- </Text>
103
- <Text
104
- as="span"
105
- className={cx(
106
- "subheading6",
107
- isDark ? "text-text-inverse" : "text-text"
108
- )}
109
- >
110
- {price.split(".")[0]}
111
- </Text>
112
- <Text
113
- as="span"
114
- className={cx(
115
- "label2",
116
- isDark ? "text-text-inverse" : "text-text"
117
- )}
118
- >
119
- {price.split(".")[1]}/mo
120
- </Text>
121
- </div>
122
- </header>
123
-
124
- {/* Description */}
125
- <section>
126
- <Text
127
- className={cx("body2", isDark ? "text-text-inverse" : "text-text")}
128
- >
129
- {description}
130
- </Text>
131
- </section>
132
-
133
- {/* Gift Badge */}
134
- {giftBadge?.length ? (
135
- <div
136
- className={cx(
137
- "flex-col items-center rounded-surface-sm p-2",
138
- isDark ? "bg-bg-surface-inverse" : "bg-bg-surface-info"
139
- )}
140
- >
141
- {giftBadge?.map((badge: any) => {
142
- return (
143
- <div key={badge.title} className="flex items-center gap-2">
144
- <Image
145
- src={badge.icon}
146
- alt={badge.title}
147
- width={24}
148
- height={24}
149
- />
150
- <Text
151
- as="div"
152
- className={cx(
153
- "body3",
154
- isDark ? "text-text-inverse" : "text-text"
155
- )}
156
- >
157
- {badge.title}
158
- </Text>
159
- </div>
160
- );
161
- })}
162
- </div>
163
- ) : null}
164
-
165
- {/* CTA */}
166
- <SelectPlanButton
167
- onSelect={onCtaClick}
168
- speed={speed}
169
- isSelected={isDark}
170
- renderCheckPlans={renderCheckPlans}
171
- cta={cta}
172
- iconSize={40}
173
- techType={techType}
174
- isMax={ismaxSpeed}
175
- />
176
- {/* Features */}
177
- {features.length > 0 && (
178
- <section className="flex flex-col gap-3">
179
- <Button
180
- onClick={handleToggle}
181
- className="group flex w-full items-center gap-2 text-left"
182
- >
183
- <Text
184
- as="h4"
185
- className={cx(
186
- "label3",
187
- isDark ? "text-text-inverse" : "text-text"
188
- )}
189
- >
190
- {featuresTitle}
191
- </Text>
192
- <MaterialIcon
193
- name="keyboard_arrow_down"
194
- fill={1}
195
- size={24}
196
- className={cx(
197
- "transition-transform duration-300",
198
- isExpanded && "rotate-180",
199
- isDark ? "text-text-inverse" : "text-text"
200
- )}
201
- />
202
- </Button>
203
- <div
204
- className={cx(
205
- "overflow-hidden transition-all duration-200 ease-in-out",
206
- isExpanded ? "opacity-100" : "max-h-0 opacity-0"
207
- )}
208
- >
209
- <div className="flex flex-col">
210
- <Checklist
211
- items={features}
212
- iconSize={24}
213
- iconPosition="top"
214
- iconClassName="mt-0"
215
- iconColor={isDark ? "yellow" : "green"}
216
- listContainerClassName="mt-0 space-y-0 flex flex-col gap-3"
217
- listItemClassName={`${isDark ? "text-text-inverse" : "text-text"}`}
218
- />
219
- </div>
220
- {innerBadge?.badgeIcon ? (
221
- <div className="flex items-center gap-2 text-center">
222
- <Image
223
- src={innerBadge.badgeIcon}
224
- alt="Inner Badge"
225
- width={12}
226
- height={12}
227
- />
228
- <span className="footnote text-text">
229
- {innerBadge.badgeText.includes("|") ? (
230
- <>
231
- <span className="font-bold">
232
- {innerBadge.badgeText.split("|")[0]}
233
- </span>
234
- <span className="font-normal">
235
- {innerBadge.badgeText.split("|")[1]}
236
- </span>
237
- </>
238
- ) : (
239
- <Text className="font-bold">{innerBadge.badgeText}</Text>
240
- )}
241
- </span>
242
- </div>
243
- ) : null}
244
- </div>
245
- </section>
246
- )}
247
- </div>
248
- </article>
249
- );
250
- };
251
- export default ProductCard;
1
+ "use client";
2
+
3
+ import React, { useState } from "react";
4
+ import { ProductCardProps } from "./types";
5
+
6
+ import { Button } from "@shared/components/button";
7
+ import { Checklist } from "@shared/components/checklist";
8
+ import { Image } from "@shared/components/image";
9
+ import { MaterialIcon } from "@shared/components/material-icon";
10
+ import { SelectPlanButton } from "@shared/components/select-plan-button";
11
+ import { Text } from "@shared/components/text";
12
+ import { cx } from "@shared/utils";
13
+
14
+ export const ProductCard: React.FC<ProductCardProps> = ({
15
+ planName,
16
+ planSubtext,
17
+ speed,
18
+ price,
19
+ description,
20
+ bestValue = false,
21
+ bestValueText = "best value",
22
+ giftBadge,
23
+ innerBadge,
24
+ theme = "light",
25
+ featuresTitle = "Business Ready Internet features",
26
+ features = [],
27
+ isExpanded: controlledExpanded,
28
+ onToggleExpand,
29
+ onCtaClick,
30
+ renderCheckPlans,
31
+ cta,
32
+ techType,
33
+ ismaxSpeed,
34
+ }) => {
35
+ const [internalExpanded, setInternalExpanded] = useState(false);
36
+ // Use parent state if provided, otherwise use internal state
37
+ const isExpanded =
38
+ controlledExpanded !== undefined ? controlledExpanded : internalExpanded;
39
+
40
+ const handleToggle = () => {
41
+ if (onToggleExpand) {
42
+ onToggleExpand();
43
+ } else {
44
+ setInternalExpanded(!internalExpanded);
45
+ }
46
+ };
47
+
48
+ const isDark = theme === "dark";
49
+
50
+ return (
51
+ <article
52
+ className={cx(
53
+ "relative flex h-full w-full max-w-[392px] flex-col",
54
+ !bestValue && "md:pt-[38px]"
55
+ )}
56
+ >
57
+ {/* Best Value Banner */}
58
+ {bestValue && (
59
+ <div className="label3 rounded-t-[20px] bg-bg-fill-brand-accent px-6 py-2 text-center text-text">
60
+ {bestValueText}
61
+ </div>
62
+ )}
63
+
64
+ {/* Main Card */}
65
+ <div
66
+ className={cx(
67
+ "flex flex-grow flex-col gap-5 rounded-card px-5 py-7 shadow-cardDrop",
68
+ isDark
69
+ ? "text-inverse border-2 border-border-accent bg-bg-fill-inverse"
70
+ : "bg-bg text-text",
71
+ bestValue ? "rounded-t-none border-t-0" : ""
72
+ )}
73
+ >
74
+ {/* Plan Name & Price */}
75
+ <header className="overflow-none flex items-start justify-between gap-4">
76
+ <div className="flex flex-col">
77
+ <Text
78
+ as="h3"
79
+ className={cx(
80
+ "subheading2",
81
+ isDark ? "text-text-accent-on-bg-inverse" : "text-text-brand"
82
+ )}
83
+ >
84
+ {planName}
85
+ </Text>
86
+ <Text
87
+ as="p"
88
+ className={`label3 text-wrap ${isDark ? "text-text-accent-on-bg-inverse" : "text-text-brand"}`}
89
+ >
90
+ {planSubtext}
91
+ </Text>
92
+ </div>
93
+ <div className="flex items-start">
94
+ <Text
95
+ as="span"
96
+ className={cx(
97
+ "label2 mr-1 mt-[2px]",
98
+ isDark ? "text-text-inverse" : "text-text"
99
+ )}
100
+ >
101
+ $
102
+ </Text>
103
+ <Text
104
+ as="span"
105
+ className={cx(
106
+ "subheading6",
107
+ isDark ? "text-text-inverse" : "text-text"
108
+ )}
109
+ >
110
+ {price.split(".")[0]}
111
+ </Text>
112
+ <Text
113
+ as="span"
114
+ className={cx(
115
+ "label2",
116
+ isDark ? "text-text-inverse" : "text-text"
117
+ )}
118
+ >
119
+ {price.split(".")[1]}/mo
120
+ </Text>
121
+ </div>
122
+ </header>
123
+
124
+ {/* Description */}
125
+ <section>
126
+ <Text
127
+ className={cx("body2", isDark ? "text-text-inverse" : "text-text")}
128
+ >
129
+ {description}
130
+ </Text>
131
+ </section>
132
+
133
+ {/* Gift Badge */}
134
+ {giftBadge?.length ? (
135
+ <div
136
+ className={cx(
137
+ "flex-col items-center rounded-surface-sm p-2",
138
+ isDark ? "bg-bg-surface-inverse" : "bg-bg-surface-info"
139
+ )}
140
+ >
141
+ {giftBadge?.map((badge: any) => {
142
+ return (
143
+ <div key={badge.title} className="flex items-center gap-2">
144
+ <Image
145
+ src={badge.icon}
146
+ alt={badge.title}
147
+ width={24}
148
+ height={24}
149
+ />
150
+ <Text
151
+ as="div"
152
+ className={cx(
153
+ "body3",
154
+ isDark ? "text-text-inverse" : "text-text"
155
+ )}
156
+ >
157
+ {badge.title}
158
+ </Text>
159
+ </div>
160
+ );
161
+ })}
162
+ </div>
163
+ ) : null}
164
+
165
+ {/* CTA */}
166
+ <SelectPlanButton
167
+ onSelect={onCtaClick}
168
+ speed={speed}
169
+ isSelected={isDark}
170
+ renderCheckPlans={renderCheckPlans}
171
+ cta={cta}
172
+ iconSize={40}
173
+ techType={techType}
174
+ isMax={ismaxSpeed}
175
+ />
176
+ {/* Features */}
177
+ {features.length > 0 && (
178
+ <section className="flex flex-col gap-3">
179
+ <Button
180
+ onClick={handleToggle}
181
+ className="group flex w-full items-center gap-2 text-left"
182
+ >
183
+ <Text
184
+ as="h4"
185
+ className={cx(
186
+ "label3",
187
+ isDark ? "text-text-inverse" : "text-text"
188
+ )}
189
+ >
190
+ {featuresTitle}
191
+ </Text>
192
+ <MaterialIcon
193
+ name="keyboard_arrow_down"
194
+ fill={1}
195
+ size={24}
196
+ className={cx(
197
+ "transition-transform duration-300",
198
+ isExpanded && "rotate-180",
199
+ isDark ? "text-text-inverse" : "text-text"
200
+ )}
201
+ />
202
+ </Button>
203
+ <div
204
+ className={cx(
205
+ "overflow-hidden transition-all duration-200 ease-in-out",
206
+ isExpanded ? "opacity-100" : "max-h-0 opacity-0"
207
+ )}
208
+ >
209
+ <div className="flex flex-col">
210
+ <Checklist
211
+ items={features}
212
+ iconSize={24}
213
+ iconPosition="top"
214
+ iconClassName="mt-0"
215
+ iconColor={isDark ? "yellow" : "green"}
216
+ listContainerClassName="mt-0 space-y-0 flex flex-col gap-3"
217
+ listItemClassName={`${isDark ? "text-text-inverse" : "text-text"}`}
218
+ />
219
+ </div>
220
+ {innerBadge?.badgeIcon ? (
221
+ <div className="flex items-center gap-2 text-center">
222
+ <Image
223
+ src={innerBadge.badgeIcon}
224
+ alt="Inner Badge"
225
+ width={12}
226
+ height={12}
227
+ />
228
+ <span className="footnote text-text">
229
+ {innerBadge.badgeText.includes("|") ? (
230
+ <>
231
+ <span className="font-bold">
232
+ {innerBadge.badgeText.split("|")[0]}
233
+ </span>
234
+ <span className="font-normal">
235
+ {innerBadge.badgeText.split("|")[1]}
236
+ </span>
237
+ </>
238
+ ) : (
239
+ <Text className="font-bold">{innerBadge.badgeText}</Text>
240
+ )}
241
+ </span>
242
+ </div>
243
+ ) : null}
244
+ </div>
245
+ </section>
246
+ )}
247
+ </div>
248
+ </article>
249
+ );
250
+ };
251
+ export default ProductCard;
@@ -1,28 +1,28 @@
1
- import React, { ReactNode } from "react";
2
-
3
- import { ButtonProps } from "@shared/contentful/blocks/button/types";
4
- import { CheckPlansProps } from "@shared/types/micro-components";
5
-
6
- export interface ProductCardProps {
7
- planName: string;
8
- planSubtext?: string;
9
- speed: string;
10
- techType?: string;
11
- ismaxSpeed?: boolean;
12
- price: string;
13
- description: string;
14
- bestValue?: boolean;
15
- bestValueText?: string;
16
- giftBadge?: [{ title: ReactNode; icon: string }];
17
- innerBadge?: { badgeText: string; badgeIcon: string };
18
- theme?: "light" | "dark";
19
- featuresTitle?: string;
20
- features: ReactNode[];
21
- isExpanded?: boolean;
22
- onToggleExpand?: () => void;
23
- ctaText?: string;
24
- cta?: ButtonProps;
25
- onCtaClick: () => void;
26
- hostType: "residential" | "smb";
27
- renderCheckPlans?: (overrides?: CheckPlansProps) => React.ReactNode;
28
- }
1
+ import React, { ReactNode } from "react";
2
+
3
+ import { ButtonProps } from "@shared/contentful/blocks/button/types";
4
+ import { CheckPlansProps } from "@shared/types/micro-components";
5
+
6
+ export interface ProductCardProps {
7
+ planName: string;
8
+ planSubtext?: string;
9
+ speed: string;
10
+ techType?: string;
11
+ ismaxSpeed?: boolean;
12
+ price: string;
13
+ description: string;
14
+ bestValue?: boolean;
15
+ bestValueText?: string;
16
+ giftBadge?: [{ title: ReactNode; icon: string }];
17
+ innerBadge?: { badgeText: string; badgeIcon: string };
18
+ theme?: "light" | "dark";
19
+ featuresTitle?: string;
20
+ features: ReactNode[];
21
+ isExpanded?: boolean;
22
+ onToggleExpand?: () => void;
23
+ ctaText?: string;
24
+ cta?: ButtonProps;
25
+ onCtaClick: () => void;
26
+ hostType: "residential" | "smb";
27
+ renderCheckPlans?: (overrides?: CheckPlansProps) => React.ReactNode;
28
+ }