@windstream/react-shared-components 0.0.92 → 0.0.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 (151) hide show
  1. package/README.md +635 -635
  2. package/dist/contentful/index.d.ts +28 -31
  3. package/dist/contentful/index.esm.js +1 -1
  4. package/dist/contentful/index.esm.js.map +1 -1
  5. package/dist/contentful/index.js +1 -1
  6. package/dist/contentful/index.js.map +1 -1
  7. package/dist/core.d.ts +1 -0
  8. package/dist/index.d.ts +4 -3
  9. package/dist/index.esm.js +4 -4
  10. package/dist/index.esm.js.map +1 -1
  11. package/dist/index.js +3 -3
  12. package/dist/index.js.map +1 -1
  13. package/dist/styles.css +1 -1
  14. package/package.json +177 -177
  15. package/src/components/accordion/Accordion.stories.tsx +230 -230
  16. package/src/components/accordion/index.tsx +9 -2
  17. package/src/components/accordion/types.ts +11 -10
  18. package/src/components/alert-card/AlertCard.stories.tsx +171 -171
  19. package/src/components/alert-card/index.tsx +33 -32
  20. package/src/components/alert-card/types.ts +11 -9
  21. package/src/components/brand-button/BrandButton.stories.tsx +223 -223
  22. package/src/components/brand-button/helpers.ts +35 -35
  23. package/src/components/brand-button/index.tsx +115 -115
  24. package/src/components/brand-button/types.ts +37 -37
  25. package/src/components/button/Button.stories.tsx +108 -108
  26. package/src/components/button/index.tsx +27 -27
  27. package/src/components/button/types.ts +14 -14
  28. package/src/components/call-button/CallButton.stories.tsx +324 -324
  29. package/src/components/call-button/index.tsx +86 -86
  30. package/src/components/call-button/types.ts +11 -11
  31. package/src/components/checkbox/Checkbox.stories.tsx +247 -247
  32. package/src/components/checkbox/index.tsx +197 -197
  33. package/src/components/checkbox/types.ts +27 -27
  34. package/src/components/checklist/Checklist.stories.tsx +150 -150
  35. package/src/components/checklist/index.tsx +55 -55
  36. package/src/components/checklist/types.ts +14 -14
  37. package/src/components/collapse/Collapse.stories.tsx +255 -255
  38. package/src/components/collapse/index.tsx +46 -46
  39. package/src/components/collapse/types.ts +6 -6
  40. package/src/components/divider/Divider.stories.tsx +205 -205
  41. package/src/components/divider/index.tsx +22 -22
  42. package/src/components/divider/type.ts +3 -3
  43. package/src/components/image/Image.stories.tsx +113 -113
  44. package/src/components/image/index.tsx +25 -25
  45. package/src/components/image/types.ts +40 -40
  46. package/src/components/input/Input.stories.tsx +325 -325
  47. package/src/components/input/index.tsx +177 -177
  48. package/src/components/input/types.ts +37 -37
  49. package/src/components/link/Link.stories.tsx +163 -163
  50. package/src/components/link/types.ts +25 -25
  51. package/src/components/list/List.stories.tsx +272 -272
  52. package/src/components/list/index.tsx +88 -88
  53. package/src/components/list/list-item/index.tsx +38 -38
  54. package/src/components/list/list-item/types.ts +13 -13
  55. package/src/components/list/types.ts +29 -29
  56. package/src/components/material-icon/MaterialIcon.stories.tsx +322 -330
  57. package/src/components/material-icon/constants.ts +98 -98
  58. package/src/components/material-icon/index.tsx +47 -44
  59. package/src/components/material-icon/types.ts +31 -31
  60. package/src/components/modal/Modal.stories.tsx +171 -171
  61. package/src/components/modal/index.tsx +164 -164
  62. package/src/components/modal/types.ts +24 -24
  63. package/src/components/next-image/index.tsx +32 -32
  64. package/src/components/next-image/types.ts +1 -1
  65. package/src/components/radio-button/RadioButton.stories.tsx +307 -307
  66. package/src/components/radio-button/index.tsx +75 -75
  67. package/src/components/radio-button/types.ts +21 -21
  68. package/src/components/see-more/SeeMore.stories.tsx +181 -181
  69. package/src/components/see-more/index.tsx +44 -44
  70. package/src/components/see-more/types.ts +4 -4
  71. package/src/components/select/Select.stories.tsx +411 -411
  72. package/src/components/select/index.tsx +150 -150
  73. package/src/components/select/types.ts +35 -35
  74. package/src/components/select-plan-button/SelectPlanButton.stories.tsx +184 -184
  75. package/src/components/select-plan-button/index.tsx +31 -31
  76. package/src/components/select-plan-button/types.ts +5 -5
  77. package/src/components/skeleton/Skeleton.stories.tsx +179 -179
  78. package/src/components/skeleton/index.tsx +61 -61
  79. package/src/components/skeleton/types.ts +4 -4
  80. package/src/components/spinner/Spinner.stories.tsx +335 -335
  81. package/src/components/spinner/index.tsx +44 -44
  82. package/src/components/spinner/types.ts +5 -5
  83. package/src/components/text/Text.stories.tsx +321 -321
  84. package/src/components/text/index.tsx +25 -25
  85. package/src/components/text/types.ts +45 -45
  86. package/src/components/tooltip/Tooltip.stories.tsx +219 -219
  87. package/src/components/tooltip/index.tsx +74 -74
  88. package/src/components/tooltip/types.ts +7 -7
  89. package/src/components/view-cart-button/ViewCartButton.stories.tsx +252 -252
  90. package/src/components/view-cart-button/index.tsx +42 -42
  91. package/src/components/view-cart-button/types.ts +5 -5
  92. package/src/contentful/blocks/accordion/index.tsx +3 -1
  93. package/src/contentful/blocks/button/Button.stories.tsx +40 -40
  94. package/src/contentful/blocks/button/index.tsx +3 -1
  95. package/src/contentful/blocks/button/types.ts +32 -31
  96. package/src/contentful/blocks/callout/Callout.stories.tsx +23 -23
  97. package/src/contentful/blocks/callout/index.tsx +66 -66
  98. package/src/contentful/blocks/cards/Cards.stories.tsx +23 -23
  99. package/src/contentful/blocks/cards/index.tsx +13 -13
  100. package/src/contentful/blocks/cards/product-card/index.tsx +199 -199
  101. package/src/contentful/blocks/cards/product-card/types.ts +21 -21
  102. package/src/contentful/blocks/cards/simple-card/index.tsx +12 -1
  103. package/src/contentful/blocks/cards/simple-card/types.ts +2 -5
  104. package/src/contentful/blocks/cards/testimonial-card/index.tsx +88 -88
  105. package/src/contentful/blocks/cards/testimonial-card/types.tsx +12 -12
  106. package/src/contentful/blocks/cards/types.ts +1 -1
  107. package/src/contentful/blocks/carousel/Carousel.stories.tsx +23 -23
  108. package/src/contentful/blocks/carousel/helper.tsx +348 -349
  109. package/src/contentful/blocks/carousel/index.tsx +68 -68
  110. package/src/contentful/blocks/carousel/types.ts +139 -139
  111. package/src/contentful/blocks/cta-callout/CtaCallout.stories.tsx +46 -46
  112. package/src/contentful/blocks/cta-callout/index.tsx +54 -54
  113. package/src/contentful/blocks/cta-callout/types.ts +22 -22
  114. package/src/contentful/blocks/find-kinetic/index.tsx +124 -124
  115. package/src/contentful/blocks/floating-banner/FloatingBanner.stories.tsx +34 -34
  116. package/src/contentful/blocks/floating-banner/types.ts +22 -22
  117. package/src/contentful/blocks/footer/Footer.stories.tsx +30 -30
  118. package/src/contentful/blocks/image-promo-bar/ImagePromoBar.stories.tsx +23 -23
  119. package/src/contentful/blocks/image-promo-bar/helper.tsx +28 -28
  120. package/src/contentful/blocks/image-promo-bar/index.tsx +231 -231
  121. package/src/contentful/blocks/image-promo-bar/types.ts +44 -44
  122. package/src/contentful/blocks/image-promo-bar/vimeo-embed.tsx +93 -93
  123. package/src/contentful/blocks/image-promo-bar/youtube-embed.tsx +46 -46
  124. package/src/contentful/blocks/modal/constants.ts +53 -53
  125. package/src/contentful/blocks/modal/index.tsx +91 -91
  126. package/src/contentful/blocks/modal/types.ts +12 -12
  127. package/src/contentful/blocks/navigation/desktop-link-groups.tsx/index.tsx +111 -111
  128. package/src/contentful/blocks/navigation/index.tsx +385 -385
  129. package/src/contentful/blocks/navigation/mobile-link-groups.tsx/index.tsx +80 -80
  130. package/src/contentful/blocks/navigation/types.ts +41 -41
  131. package/src/contentful/blocks/primary-hero/PrimaryHero.stories.tsx +23 -23
  132. package/src/contentful/blocks/primary-hero/index.tsx +228 -228
  133. package/src/contentful/blocks/primary-hero/types.ts +35 -35
  134. package/src/contentful/blocks/shape-background-wrapper/ShapeBackgroundWrapper.stories.tsx +26 -26
  135. package/src/contentful/blocks/shape-background-wrapper/index.tsx +124 -124
  136. package/src/contentful/blocks/shape-background-wrapper/types.ts +36 -36
  137. package/src/contentful/blocks/text/Text.stories.tsx +23 -23
  138. package/src/contentful/blocks/text/index.tsx +12 -12
  139. package/src/contentful/blocks/text/types.ts +1 -1
  140. package/src/contentful/index.ts +57 -57
  141. package/src/hooks/use-body-scroll-lock.ts +34 -34
  142. package/src/hooks/use-outside-click.ts +17 -17
  143. package/src/index.ts +96 -96
  144. package/src/next/index.ts +5 -5
  145. package/src/setupTests.ts +46 -46
  146. package/src/stories/DocsTemplate.tsx +24 -24
  147. package/src/styles/globals.css +343 -307
  148. package/src/types/global.d.ts +9 -9
  149. package/src/types/micro-components.ts +89 -89
  150. package/src/utils/index.ts +49 -49
  151. package/tailwind.config.cjs +29 -0
@@ -1,199 +1,199 @@
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
- hostType,
31
- }) => {
32
- const [internalExpanded, setInternalExpanded] = useState(false);
33
- // Use parent state if provided, otherwise use internal state
34
- const isExpanded =
35
- controlledExpanded !== undefined ? controlledExpanded : internalExpanded;
36
-
37
- const handleToggle = () => {
38
- if (onToggleExpand) {
39
- onToggleExpand();
40
- } else {
41
- setInternalExpanded(!internalExpanded);
42
- }
43
- };
44
-
45
- const isDark = theme === "dark";
46
-
47
- return (
48
- <article
49
- className={cx(
50
- "relative flex h-full w-full max-w-[392px] flex-col",
51
- !bestValue && "pt-[28px]"
52
- )}
53
- >
54
- {/* Best Value Banner */}
55
- {bestValue && (
56
- <div className="rounded-t-[20px] bg-bg-fill-brand-accent px-4 py-1 text-center text-sm font-bold text-text">
57
- {bestValueText}
58
- </div>
59
- )}
60
-
61
- {/* Main Card */}
62
- <div
63
- className={cx(
64
- "flex flex-grow flex-col gap-6 rounded-[20px] px-5 shadow-cardDrop",
65
- isDark ? "bg-bg-fill-inverse text-white" : "bg-white text-text",
66
- bestValue ? "rounded-t-none" : ""
67
- )}
68
- >
69
- {/* Plan Name & Price */}
70
- <header className="overflow-none flex items-start justify-between gap-4 pt-7">
71
- <div className="flex flex-col">
72
- <Text
73
- as="h3"
74
- className={cx(
75
- "whitespace-nowrap text-subheading2 font-black lowercase",
76
- isDark ? "text-bg-fill-brand-accent" : "text-text-brand"
77
- )}
78
- >
79
- {planName}
80
- </Text>
81
- <Text
82
- as="p"
83
- className={`text-label3 font-black ${isDark ? "text-bg-fill-brand-accent" : "text-text-brand"} lowercase md:whitespace-nowrap`}
84
- >
85
- {planSubtext}
86
- </Text>
87
- </div>
88
- <div className="flex items-start">
89
- <span className="text-xl font-normal">$</span>
90
- <span className="text-subheading6 font-medium leading-none">
91
- {price.split(".")[0]}
92
- </span>
93
- <span className="text-sm font-black">{price.split(".")[1]}/mo</span>
94
- </div>
95
- </header>
96
-
97
- {/* Description */}
98
- <section>
99
- <Text
100
- className={cx("text-base", isDark ? "text-white" : "text-text")}
101
- >
102
- {description}
103
- </Text>
104
- </section>
105
-
106
- {/* Gift Badge */}
107
- <div
108
- className={cx(
109
- "flex-col items-center rounded-xl p-2",
110
- isDark ? "bg-bg-surface-inverse" : "bg-gray-200"
111
- )}
112
- >
113
- {giftBadge &&
114
- giftBadge?.map((badge: any) => {
115
- return (
116
- <div key={badge.title} className="flex items-center gap-2 p-1">
117
- <Image
118
- src={badge.icon}
119
- alt={badge.title}
120
- width={24}
121
- height={24}
122
- />
123
- <Text className="text-sm font-bold">{badge.title}</Text>
124
- </div>
125
- );
126
- })}
127
- </div>
128
-
129
- {/* CTA */}
130
- <SelectPlanButton
131
- onSelect={onCtaClick}
132
- speed={speed}
133
- isSelected={isDark}
134
- />
135
- {/* Features */}
136
- {features.length > 0 && (
137
- <section className="flex flex-col gap-6">
138
- <Button
139
- onClick={handleToggle}
140
- className="group flex w-full items-center justify-between text-left"
141
- >
142
- <Text as="h4" className="text-base font-bold">
143
- {featuresTitle}
144
- </Text>
145
- <MaterialIcon
146
- name="keyboard_arrow_up"
147
- fill={1}
148
- size={24}
149
- className={cx(
150
- "transition-transform duration-300",
151
- isExpanded && "rotate-180"
152
- )}
153
- />
154
- </Button>
155
- <div
156
- className={cx(
157
- "overflow-hidden transition-all duration-300 ease-in-out",
158
- isExpanded ? "pb-7 opacity-100" : "max-h-0 opacity-0"
159
- )}
160
- >
161
- <div className="flex flex-col gap-3">
162
- <Checklist
163
- items={features}
164
- iconColor={isDark ? "yellow" : "green"}
165
- listItemClassName={`${isDark ? "text-white" : "text-text"}`}
166
- />
167
- </div>
168
- {innerBadge?.badgeIcon ? (
169
- <div className="flex items-center gap-2 text-center">
170
- <Image
171
- src={innerBadge.badgeIcon}
172
- alt="Inner Badge"
173
- width={12}
174
- height={12}
175
- />
176
- <span className="text-sm">
177
- {innerBadge.badgeText.includes("|") ? (
178
- <>
179
- <span className="font-bold">
180
- {innerBadge.badgeText.split("|")[0]}
181
- </span>
182
- <span className="font-normal">
183
- {innerBadge.badgeText.split("|")[1]}
184
- </span>
185
- </>
186
- ) : (
187
- <Text className="font-bold">{innerBadge.badgeText}</Text>
188
- )}
189
- </span>
190
- </div>
191
- ) : null}
192
- </div>
193
- </section>
194
- )}
195
- </div>
196
- </article>
197
- );
198
- };
199
- 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
+ hostType,
31
+ }) => {
32
+ const [internalExpanded, setInternalExpanded] = useState(false);
33
+ // Use parent state if provided, otherwise use internal state
34
+ const isExpanded =
35
+ controlledExpanded !== undefined ? controlledExpanded : internalExpanded;
36
+
37
+ const handleToggle = () => {
38
+ if (onToggleExpand) {
39
+ onToggleExpand();
40
+ } else {
41
+ setInternalExpanded(!internalExpanded);
42
+ }
43
+ };
44
+
45
+ const isDark = theme === "dark";
46
+
47
+ return (
48
+ <article
49
+ className={cx(
50
+ "relative flex h-full w-full max-w-[392px] flex-col",
51
+ !bestValue && "pt-[28px]"
52
+ )}
53
+ >
54
+ {/* Best Value Banner */}
55
+ {bestValue && (
56
+ <div className="rounded-t-[20px] bg-bg-fill-brand-accent px-4 py-1 text-center text-sm font-bold text-text">
57
+ {bestValueText}
58
+ </div>
59
+ )}
60
+
61
+ {/* Main Card */}
62
+ <div
63
+ className={cx(
64
+ "flex flex-grow flex-col gap-6 rounded-[20px] px-5 shadow-cardDrop",
65
+ isDark ? "bg-bg-fill-inverse text-white" : "bg-white text-text",
66
+ bestValue ? "rounded-t-none" : ""
67
+ )}
68
+ >
69
+ {/* Plan Name & Price */}
70
+ <header className="overflow-none flex items-start justify-between gap-4 pt-7">
71
+ <div className="flex flex-col">
72
+ <Text
73
+ as="h3"
74
+ className={cx(
75
+ "subheading2 whitespace-nowrap font-black",
76
+ isDark ? "text-bg-fill-brand-accent" : "text-text-brand"
77
+ )}
78
+ >
79
+ {planName}
80
+ </Text>
81
+ <Text
82
+ as="p"
83
+ className={`text-label3 font-black ${isDark ? "text-bg-fill-brand-accent" : "text-text-brand"} md:whitespace-nowrap`}
84
+ >
85
+ {planSubtext}
86
+ </Text>
87
+ </div>
88
+ <div className="flex items-start">
89
+ <span className="text-xl font-normal">$</span>
90
+ <span className="text-subheading6 font-medium leading-none">
91
+ {price.split(".")[0]}
92
+ </span>
93
+ <span className="text-sm font-black">{price.split(".")[1]}/mo</span>
94
+ </div>
95
+ </header>
96
+
97
+ {/* Description */}
98
+ <section>
99
+ <Text
100
+ className={cx("text-base", isDark ? "text-white" : "text-text")}
101
+ >
102
+ {description}
103
+ </Text>
104
+ </section>
105
+
106
+ {/* Gift Badge */}
107
+ <div
108
+ className={cx(
109
+ "flex-col items-center rounded-xl p-2",
110
+ isDark ? "bg-bg-surface-inverse" : "bg-gray-200"
111
+ )}
112
+ >
113
+ {giftBadge &&
114
+ giftBadge?.map((badge: any) => {
115
+ return (
116
+ <div key={badge.title} className="flex items-center gap-2 p-1">
117
+ <Image
118
+ src={badge.icon}
119
+ alt={badge.title}
120
+ width={24}
121
+ height={24}
122
+ />
123
+ <Text className="text-sm font-bold">{badge.title}</Text>
124
+ </div>
125
+ );
126
+ })}
127
+ </div>
128
+
129
+ {/* CTA */}
130
+ <SelectPlanButton
131
+ onSelect={onCtaClick}
132
+ speed={speed}
133
+ isSelected={isDark}
134
+ />
135
+ {/* Features */}
136
+ {features.length > 0 && (
137
+ <section className="flex flex-col gap-6">
138
+ <Button
139
+ onClick={handleToggle}
140
+ className="group flex w-full items-center justify-between text-left"
141
+ >
142
+ <Text as="h4" className="text-base font-bold">
143
+ {featuresTitle}
144
+ </Text>
145
+ <MaterialIcon
146
+ name="keyboard_arrow_up"
147
+ fill={1}
148
+ size={24}
149
+ className={cx(
150
+ "transition-transform duration-300",
151
+ isExpanded && "rotate-180"
152
+ )}
153
+ />
154
+ </Button>
155
+ <div
156
+ className={cx(
157
+ "overflow-hidden transition-all duration-300 ease-in-out",
158
+ isExpanded ? "pb-7 opacity-100" : "max-h-0 opacity-0"
159
+ )}
160
+ >
161
+ <div className="flex flex-col gap-3">
162
+ <Checklist
163
+ items={features}
164
+ iconColor={isDark ? "yellow" : "green"}
165
+ listItemClassName={`${isDark ? "text-white" : "text-text"}`}
166
+ />
167
+ </div>
168
+ {innerBadge?.badgeIcon ? (
169
+ <div className="flex items-center gap-2 text-center">
170
+ <Image
171
+ src={innerBadge.badgeIcon}
172
+ alt="Inner Badge"
173
+ width={12}
174
+ height={12}
175
+ />
176
+ <span className="text-sm">
177
+ {innerBadge.badgeText.includes("|") ? (
178
+ <>
179
+ <span className="font-bold">
180
+ {innerBadge.badgeText.split("|")[0]}
181
+ </span>
182
+ <span className="font-normal">
183
+ {innerBadge.badgeText.split("|")[1]}
184
+ </span>
185
+ </>
186
+ ) : (
187
+ <Text className="font-bold">{innerBadge.badgeText}</Text>
188
+ )}
189
+ </span>
190
+ </div>
191
+ ) : null}
192
+ </div>
193
+ </section>
194
+ )}
195
+ </div>
196
+ </article>
197
+ );
198
+ };
199
+ export default ProductCard;
@@ -1,21 +1,21 @@
1
- import { ReactNode } from "react";
2
-
3
- export interface ProductCardProps {
4
- planName: string;
5
- planSubtext?: string;
6
- speed: string;
7
- price: string;
8
- description: string;
9
- bestValue?: boolean;
10
- bestValueText?: string;
11
- giftBadge?: [{ title: ReactNode; icon: string }];
12
- innerBadge?: { badgeText: string; badgeIcon: string };
13
- theme?: "light" | "dark";
14
- featuresTitle?: string;
15
- features: ReactNode[];
16
- isExpanded?: boolean;
17
- onToggleExpand?: () => void;
18
- ctaText?: string;
19
- onCtaClick: () => void;
20
- hostType: "residential" | "smb";
21
- }
1
+ import { ReactNode } from "react";
2
+
3
+ export interface ProductCardProps {
4
+ planName: string;
5
+ planSubtext?: string;
6
+ speed: string;
7
+ price: string;
8
+ description: string;
9
+ bestValue?: boolean;
10
+ bestValueText?: string;
11
+ giftBadge?: [{ title: ReactNode; icon: string }];
12
+ innerBadge?: { badgeText: string; badgeIcon: string };
13
+ theme?: "light" | "dark";
14
+ featuresTitle?: string;
15
+ features: ReactNode[];
16
+ isExpanded?: boolean;
17
+ onToggleExpand?: () => void;
18
+ ctaText?: string;
19
+ onCtaClick: () => void;
20
+ hostType: "residential" | "smb";
21
+ }
@@ -1,4 +1,5 @@
1
1
  import React from "react";
2
+ import { Button } from "../../button";
2
3
  import { backgroundColorMap, Item, SimpleCardProps } from "./types";
3
4
 
4
5
  import { NextImage } from "@shared/components/next-image";
@@ -53,7 +54,7 @@ export const SimpleCard: React.FC<SimpleCardProps> = ({
53
54
  className={`callout-card ${mdWidth} ${lgWidth} w-full ${backgroundColorMap[card.backgroundColor ?? ""] ?? ""}`}
54
55
  >
55
56
  <div
56
- className={`card-template flex h-full flex-col items-center gap-6 p-8 text-center ${imageAlignClass}`}
57
+ className={`card-template flex h-full flex-col items-center gap-8 p-8 text-center ${imageAlignClass}`}
57
58
  >
58
59
  <div className="card-header">{imageRenderer(card)}</div>
59
60
 
@@ -69,6 +70,16 @@ export const SimpleCard: React.FC<SimpleCardProps> = ({
69
70
  </Text>
70
71
  )}
71
72
  </div>
73
+ {card.cta && (
74
+ <div className="card-footer">
75
+ <Button
76
+ linkVariant="unstyled"
77
+ linkClassName="label1 flex items-center text-text gap-2"
78
+ {...card.cta}
79
+ iconName={"expand_circle_right"}
80
+ />
81
+ </div>
82
+ )}
72
83
  </div>
73
84
  </div>
74
85
  );
@@ -1,15 +1,12 @@
1
1
  import { ReactNode } from "react";
2
+ import { ButtonProps } from "../../button/types";
2
3
 
3
4
  export type Item = {
4
5
  title?: string;
5
6
  image?: string;
6
7
  imageAlignment?: "left" | "right" | "center";
7
8
  imageView?: "standard" | "icon" | "full";
8
- cta?: {
9
- label?: string;
10
- url?: string;
11
- newTab?: boolean;
12
- };
9
+ cta?: ButtonProps;
13
10
  ctaAlignment?: "left" | "right" | "center";
14
11
  body?: ReactNode;
15
12
  backgroundColor?: string;
@@ -1,88 +1,88 @@
1
- import React from "react";
2
- import { TestimonialCardProps } from "./types";
3
- import clsx from "clsx";
4
- import Image from "next/image";
5
-
6
- import { MaterialIcon } from "@shared/components/material-icon";
7
- import { Text } from "@shared/components/text";
8
-
9
- export const TestimonialCard: React.FC<TestimonialCardProps> = ({
10
- title,
11
- quote,
12
- rating,
13
- author,
14
- role,
15
- avatarUrl,
16
- isActive = false,
17
- className,
18
- }) => {
19
- return (
20
- <figure
21
- className={clsx(
22
- "flex h-full w-full flex-col rounded-3xl p-6 font-sans transition-all duration-300 md:p-13",
23
- isActive ? "bg-white shadow-lg" : "bg-gray-50 opacity-60", // Active vs Inactive styles
24
- className
25
- )}
26
- >
27
- <header>
28
- <Text as="h3" className="mb-4 text-xl font-black lowercase text-text">
29
- {title}
30
- </Text>
31
- </header>
32
- <blockquote className="mb-5 flex-grow text-xl leading-relaxed text-text">
33
- {quote}
34
- </blockquote>
35
-
36
- {/* Rating Stars */}
37
- {rating ? (
38
- <div className="mb-5 flex gap-1">
39
- {[...Array(5)].map((_, i) => (
40
- <MaterialIcon
41
- key={i}
42
- size={24}
43
- name="star"
44
- fill={1}
45
- className={clsx(
46
- "h-5 w-5",
47
- i < rating ? "text-text" : "text-gray-300"
48
- )}
49
- aria-hidden="true"
50
- />
51
- ))}
52
- </div>
53
- ) : null}
54
-
55
- {/* Author Block */}
56
- {author && (
57
- <figcaption className="flex items-center gap-3">
58
- <div className="relative h-10 w-10 shrink-0 overflow-hidden rounded-full bg-gray-300">
59
- {avatarUrl ? (
60
- <Image
61
- src={avatarUrl}
62
- alt={author}
63
- fill={true}
64
- className="object-cover"
65
- sizes="40px"
66
- />
67
- ) : (
68
- <div className="flex h-full w-full items-center justify-center bg-gray-500 text-xs font-bold text-white">
69
- {author.charAt(0)}
70
- </div>
71
- )}
72
- </div>
73
-
74
- <div className="flex flex-col">
75
- <cite className="text-sm font-bold not-italic text-text">
76
- {author}
77
- </cite>
78
- <Text as="p" className="text-xs text-text">
79
- {role}
80
- </Text>
81
- </div>
82
- </figcaption>
83
- )}
84
- </figure>
85
- );
86
- };
87
-
88
- export default TestimonialCard;
1
+ import React from "react";
2
+ import { TestimonialCardProps } from "./types";
3
+ import clsx from "clsx";
4
+ import Image from "next/image";
5
+
6
+ import { MaterialIcon } from "@shared/components/material-icon";
7
+ import { Text } from "@shared/components/text";
8
+
9
+ export const TestimonialCard: React.FC<TestimonialCardProps> = ({
10
+ title,
11
+ quote,
12
+ rating,
13
+ author,
14
+ role,
15
+ avatarUrl,
16
+ isActive = false,
17
+ className,
18
+ }) => {
19
+ return (
20
+ <figure
21
+ className={clsx(
22
+ "flex h-full w-full flex-col rounded-3xl p-6 font-sans transition-all duration-300 md:p-13",
23
+ isActive ? "bg-white shadow-lg" : "bg-gray-50 opacity-60", // Active vs Inactive styles
24
+ className
25
+ )}
26
+ >
27
+ <header>
28
+ <Text as="h3" className="mb-4 text-xl font-black lowercase text-text">
29
+ {title}
30
+ </Text>
31
+ </header>
32
+ <blockquote className="mb-5 flex-grow text-xl leading-relaxed text-text">
33
+ {quote}
34
+ </blockquote>
35
+
36
+ {/* Rating Stars */}
37
+ {rating ? (
38
+ <div className="mb-5 flex gap-1">
39
+ {[...Array(5)].map((_, i) => (
40
+ <MaterialIcon
41
+ key={i}
42
+ size={24}
43
+ name="star"
44
+ fill={1}
45
+ className={clsx(
46
+ "h-5 w-5",
47
+ i < rating ? "text-text" : "text-gray-300"
48
+ )}
49
+ aria-hidden="true"
50
+ />
51
+ ))}
52
+ </div>
53
+ ) : null}
54
+
55
+ {/* Author Block */}
56
+ {author && (
57
+ <figcaption className="flex items-center gap-3">
58
+ <div className="relative h-10 w-10 shrink-0 overflow-hidden rounded-full bg-gray-300">
59
+ {avatarUrl ? (
60
+ <Image
61
+ src={avatarUrl}
62
+ alt={author}
63
+ fill={true}
64
+ className="object-cover"
65
+ sizes="40px"
66
+ />
67
+ ) : (
68
+ <div className="flex h-full w-full items-center justify-center bg-gray-500 text-xs font-bold text-white">
69
+ {author.charAt(0)}
70
+ </div>
71
+ )}
72
+ </div>
73
+
74
+ <div className="flex flex-col">
75
+ <cite className="text-sm font-bold not-italic text-text">
76
+ {author}
77
+ </cite>
78
+ <Text as="p" className="text-xs text-text">
79
+ {role}
80
+ </Text>
81
+ </div>
82
+ </figcaption>
83
+ )}
84
+ </figure>
85
+ );
86
+ };
87
+
88
+ export default TestimonialCard;