@windstream/react-shared-components 0.2.89 → 0.2.91
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.
- package/dist/contentful/index.d.ts +14 -0
- package/dist/contentful/index.esm.js +2 -2
- package/dist/contentful/index.esm.js.map +1 -1
- package/dist/contentful/index.js +2 -2
- package/dist/contentful/index.js.map +1 -1
- package/dist/core.d.ts +3 -3
- package/dist/index.d.ts +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/src/contentful/blocks/anchored-bottom-banner/index.test.tsx +55 -0
- package/src/contentful/blocks/anchored-bottom-banner/index.tsx +15 -2
- package/src/contentful/blocks/anchored-bottom-banner/types.ts +5 -0
- package/src/contentful/blocks/award-marquee/index.test.tsx +75 -0
- package/src/contentful/blocks/award-marquee/index.tsx +40 -19
- package/src/contentful/blocks/award-marquee/types.ts +6 -0
- package/src/contentful/blocks/callout/{Callout.stories.tsx → callout.stories.tsx} +1 -1
- package/src/contentful/blocks/cart-retention-banner/index.test.tsx +63 -3
- package/src/contentful/blocks/cart-retention-banner/index.tsx +21 -8
- package/src/contentful/blocks/cart-retention-banner/types.ts +4 -0
- package/src/contentful/blocks/floating-banner/index.test.tsx +54 -2
- package/src/contentful/blocks/floating-banner/index.tsx +13 -2
- package/src/contentful/blocks/floating-banner/types.ts +6 -0
- package/src/contentful/blocks/image-promo-bar/index.test.tsx +176 -18
- package/src/contentful/blocks/image-promo-bar/index.tsx +45 -27
- package/src/contentful/blocks/image-promo-bar/types.ts +19 -1
- package/src/contentful/blocks/navigation/{Navigation.stories.tsx → navigation.stories.tsx} +1 -1
- package/src/contentful/blocks/primary-hero/index.tsx +1 -1
- package/tailwind.config.cjs +1 -1
|
@@ -7,22 +7,42 @@ import { render, screen } from "@testing-library/react";
|
|
|
7
7
|
jest.mock("../button", () => ({
|
|
8
8
|
__esModule: true,
|
|
9
9
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
10
|
-
Button: ({
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
10
|
+
Button: ({
|
|
11
|
+
buttonLabel,
|
|
12
|
+
disableAnimation,
|
|
13
|
+
fieldProps,
|
|
14
|
+
preDefinedFunctionExecution,
|
|
15
|
+
renderCheckPlans,
|
|
16
|
+
}: any) =>
|
|
17
|
+
preDefinedFunctionExecution === "check availability" && renderCheckPlans ? (
|
|
18
|
+
renderCheckPlans()
|
|
19
|
+
) : (
|
|
20
|
+
<button
|
|
21
|
+
data-testid="cta-button"
|
|
22
|
+
data-disable-animation={String(disableAnimation)}
|
|
23
|
+
{...fieldProps}
|
|
24
|
+
>
|
|
25
|
+
{buttonLabel || "CTA"}
|
|
26
|
+
</button>
|
|
27
|
+
),
|
|
28
|
+
default: ({
|
|
29
|
+
buttonLabel,
|
|
30
|
+
disableAnimation,
|
|
31
|
+
fieldProps,
|
|
32
|
+
preDefinedFunctionExecution,
|
|
33
|
+
renderCheckPlans,
|
|
34
|
+
}: any) =>
|
|
35
|
+
preDefinedFunctionExecution === "check availability" && renderCheckPlans ? (
|
|
36
|
+
renderCheckPlans()
|
|
37
|
+
) : (
|
|
38
|
+
<button
|
|
39
|
+
data-testid="cta-button"
|
|
40
|
+
data-disable-animation={String(disableAnimation)}
|
|
41
|
+
{...fieldProps}
|
|
42
|
+
>
|
|
43
|
+
{buttonLabel || "CTA"}
|
|
44
|
+
</button>
|
|
45
|
+
),
|
|
26
46
|
}));
|
|
27
47
|
|
|
28
48
|
jest.mock("@shared/components/video-link", () => ({
|
|
@@ -63,8 +83,10 @@ jest.mock("@shared/components/next-image", () => ({
|
|
|
63
83
|
}));
|
|
64
84
|
|
|
65
85
|
jest.mock("@shared/components/text", () => ({
|
|
66
|
-
Text: ({ as: Tag = "span", children, className }: any) => (
|
|
67
|
-
<Tag className={className}
|
|
86
|
+
Text: ({ as: Tag = "span", children, className, ...rest }: any) => (
|
|
87
|
+
<Tag className={className} {...rest}>
|
|
88
|
+
{children}
|
|
89
|
+
</Tag>
|
|
68
90
|
),
|
|
69
91
|
}));
|
|
70
92
|
|
|
@@ -94,6 +116,142 @@ const defaultProps: ImagePromoBarProps = {
|
|
|
94
116
|
};
|
|
95
117
|
|
|
96
118
|
describe("ImagePromoBar", () => {
|
|
119
|
+
it("tags block content, CTAs, disclaimers, checklist and image links", () => {
|
|
120
|
+
const tag = (field: string) => ({
|
|
121
|
+
"data-contentful-field-id": field,
|
|
122
|
+
"data-contentful-entry-id": "image-promo",
|
|
123
|
+
"data-contentful-locale": "en-US",
|
|
124
|
+
});
|
|
125
|
+
const { container } = render(
|
|
126
|
+
<ImagePromoBar
|
|
127
|
+
{...defaultProps}
|
|
128
|
+
brow="Brow"
|
|
129
|
+
title="Title"
|
|
130
|
+
subTitle="Subtitle"
|
|
131
|
+
description="Description"
|
|
132
|
+
checklist={["Check"]}
|
|
133
|
+
ctaDisclaimer="CTA disclaimer"
|
|
134
|
+
disclaimer="Disclaimer"
|
|
135
|
+
imageLinks={[
|
|
136
|
+
{
|
|
137
|
+
url: "/image",
|
|
138
|
+
image: "/image.png",
|
|
139
|
+
fieldProps: {
|
|
140
|
+
image: {
|
|
141
|
+
...tag("image"),
|
|
142
|
+
"data-contentful-entry-id": "image-link-item",
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
]}
|
|
147
|
+
cta={{
|
|
148
|
+
buttonLabel: "Primary",
|
|
149
|
+
fieldProps: { "data-contentful-field-id": "nestedPrimaryCta" },
|
|
150
|
+
}}
|
|
151
|
+
secondaryCta={{
|
|
152
|
+
buttonLabel: "Secondary",
|
|
153
|
+
fieldProps: { "data-contentful-field-id": "nestedSecondaryCta" },
|
|
154
|
+
}}
|
|
155
|
+
fieldProps={{
|
|
156
|
+
brow: tag("brow"),
|
|
157
|
+
title: tag("title"),
|
|
158
|
+
subTitle: tag("subTitle"),
|
|
159
|
+
description: tag("description"),
|
|
160
|
+
checklist: tag("checklist"),
|
|
161
|
+
ctaDisclaimer: tag("ctaDisclaimer"),
|
|
162
|
+
cta: tag("cta"),
|
|
163
|
+
secondaryCta: tag("secondaryCta"),
|
|
164
|
+
disclaimer: tag("disclaimer"),
|
|
165
|
+
imageLinks: tag("imageLinks"),
|
|
166
|
+
}}
|
|
167
|
+
/>
|
|
168
|
+
);
|
|
169
|
+
[
|
|
170
|
+
"brow",
|
|
171
|
+
"title",
|
|
172
|
+
"subTitle",
|
|
173
|
+
"description",
|
|
174
|
+
"checklist",
|
|
175
|
+
"ctaDisclaimer",
|
|
176
|
+
"cta",
|
|
177
|
+
"secondaryCta",
|
|
178
|
+
"disclaimer",
|
|
179
|
+
"imageLinks",
|
|
180
|
+
].forEach(field => {
|
|
181
|
+
expect(
|
|
182
|
+
container.querySelector(`[data-contentful-field-id="${field}"]`)
|
|
183
|
+
).toHaveAttribute("data-contentful-entry-id", "image-promo");
|
|
184
|
+
});
|
|
185
|
+
expect(
|
|
186
|
+
container.querySelector(
|
|
187
|
+
'[data-contentful-entry-id="image-link-item"][data-contentful-field-id="image"]'
|
|
188
|
+
)
|
|
189
|
+
).toBeInTheDocument();
|
|
190
|
+
expect(screen.getAllByTestId("cta-button")[0]).toHaveAttribute(
|
|
191
|
+
"data-contentful-field-id",
|
|
192
|
+
"nestedPrimaryCta"
|
|
193
|
+
);
|
|
194
|
+
expect(screen.getAllByTestId("cta-button")[1]).toHaveAttribute(
|
|
195
|
+
"data-contentful-field-id",
|
|
196
|
+
"nestedSecondaryCta"
|
|
197
|
+
);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
it("applies block CTA tags to wrappers around custom check-plans content", () => {
|
|
201
|
+
const tag = (field: string) => ({
|
|
202
|
+
"data-contentful-field-id": field,
|
|
203
|
+
"data-contentful-entry-id": "image-promo",
|
|
204
|
+
});
|
|
205
|
+
const { container } = render(
|
|
206
|
+
<ImagePromoBar
|
|
207
|
+
{...defaultProps}
|
|
208
|
+
cta={{
|
|
209
|
+
preDefinedFunctionExecution: "check availability",
|
|
210
|
+
fieldProps: { "data-contentful-field-id": "primaryButton" },
|
|
211
|
+
}}
|
|
212
|
+
secondaryCta={{
|
|
213
|
+
preDefinedFunctionExecution: "check availability",
|
|
214
|
+
fieldProps: { "data-contentful-field-id": "secondaryButton" },
|
|
215
|
+
}}
|
|
216
|
+
renderCheckPlans={() => <button>Check availability</button>}
|
|
217
|
+
fieldProps={{
|
|
218
|
+
cta: tag("cta"),
|
|
219
|
+
secondaryCta: tag("secondaryCta"),
|
|
220
|
+
}}
|
|
221
|
+
/>
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
const primaryWrapper = container.querySelector(".primary-cta");
|
|
225
|
+
const secondaryWrapper = container.querySelector(".secondary-cta");
|
|
226
|
+
expect(primaryWrapper).toHaveAttribute("data-contentful-field-id", "cta");
|
|
227
|
+
expect(secondaryWrapper).toHaveAttribute(
|
|
228
|
+
"data-contentful-field-id",
|
|
229
|
+
"secondaryCta"
|
|
230
|
+
);
|
|
231
|
+
expect(primaryWrapper).toHaveAttribute(
|
|
232
|
+
"data-contentful-entry-id",
|
|
233
|
+
"image-promo"
|
|
234
|
+
);
|
|
235
|
+
expect(secondaryWrapper).toHaveAttribute(
|
|
236
|
+
"data-contentful-entry-id",
|
|
237
|
+
"image-promo"
|
|
238
|
+
);
|
|
239
|
+
expect(primaryWrapper?.querySelector("button")).toHaveTextContent(
|
|
240
|
+
"Check availability"
|
|
241
|
+
);
|
|
242
|
+
expect(secondaryWrapper?.querySelector("button")).toHaveTextContent(
|
|
243
|
+
"Check availability"
|
|
244
|
+
);
|
|
245
|
+
expect(primaryWrapper?.querySelector("button")).not.toHaveAttribute(
|
|
246
|
+
"data-contentful-field-id",
|
|
247
|
+
"primaryButton"
|
|
248
|
+
);
|
|
249
|
+
expect(secondaryWrapper?.querySelector("button")).not.toHaveAttribute(
|
|
250
|
+
"data-contentful-field-id",
|
|
251
|
+
"secondaryButton"
|
|
252
|
+
);
|
|
253
|
+
});
|
|
254
|
+
|
|
97
255
|
describe("Text content", () => {
|
|
98
256
|
it("renders brow text when provided", () => {
|
|
99
257
|
render(<ImagePromoBar {...defaultProps} brow="New" />);
|
|
@@ -37,6 +37,7 @@ export const ImagePromoBar: React.FC<ImagePromoBarProps> = ({
|
|
|
37
37
|
onVideoEvent,
|
|
38
38
|
renderCheckPlans,
|
|
39
39
|
enableAnimation = false,
|
|
40
|
+
fieldProps,
|
|
40
41
|
}) => {
|
|
41
42
|
const hasVideoLink = Boolean(videoLink);
|
|
42
43
|
|
|
@@ -56,6 +57,7 @@ export const ImagePromoBar: React.FC<ImagePromoBarProps> = ({
|
|
|
56
57
|
<Text
|
|
57
58
|
as="div"
|
|
58
59
|
className="subheading4 mb-4 text-text-brand xl:subheading2 xl:mb-3"
|
|
60
|
+
{...fieldProps?.brow}
|
|
59
61
|
>
|
|
60
62
|
{brow}
|
|
61
63
|
</Text>
|
|
@@ -64,6 +66,7 @@ export const ImagePromoBar: React.FC<ImagePromoBarProps> = ({
|
|
|
64
66
|
<Text
|
|
65
67
|
as={enableHeading ? "h1" : "h2"}
|
|
66
68
|
className="heading2 xl:heading1"
|
|
69
|
+
{...fieldProps?.title}
|
|
67
70
|
>
|
|
68
71
|
{title}
|
|
69
72
|
</Text>
|
|
@@ -72,6 +75,7 @@ export const ImagePromoBar: React.FC<ImagePromoBarProps> = ({
|
|
|
72
75
|
<Text
|
|
73
76
|
as={enableHeading ? "h2" : "h3"}
|
|
74
77
|
className="subheading3 mt-3 md:subheading1"
|
|
78
|
+
{...fieldProps?.subTitle}
|
|
75
79
|
>
|
|
76
80
|
{subTitle}
|
|
77
81
|
</Text>
|
|
@@ -79,45 +83,54 @@ export const ImagePromoBar: React.FC<ImagePromoBarProps> = ({
|
|
|
79
83
|
</div>
|
|
80
84
|
{/* Content Section */}
|
|
81
85
|
{description && (
|
|
82
|
-
<Text as="div" className="body1">
|
|
86
|
+
<Text as="div" className="body1" {...fieldProps?.description}>
|
|
83
87
|
{description}
|
|
84
88
|
</Text>
|
|
85
89
|
)}
|
|
86
90
|
{/* Checklist Rendering */}
|
|
87
91
|
{checklist.length > 0 && (
|
|
88
|
-
<
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
92
|
+
<div {...fieldProps?.checklist}>
|
|
93
|
+
<Checklist
|
|
94
|
+
items={checklist}
|
|
95
|
+
iconPosition="top"
|
|
96
|
+
iconSize={24}
|
|
97
|
+
listItemClassName="body1 text-text"
|
|
98
|
+
listContainerClassName="mt-0 space-y-0 flex flex-col gap-3"
|
|
99
|
+
/>
|
|
100
|
+
</div>
|
|
101
|
+
)}
|
|
102
|
+
{ctaDisclaimer && (
|
|
103
|
+
<div {...fieldProps?.ctaDisclaimer}>{ctaDisclaimer}</div>
|
|
95
104
|
)}
|
|
96
|
-
{ctaDisclaimer && <div>{ctaDisclaimer}</div>}
|
|
97
105
|
{imageLinks.length > 0 && (
|
|
98
|
-
<div className="flex gap-4">
|
|
106
|
+
<div className="flex gap-4" {...fieldProps?.imageLinks}>
|
|
99
107
|
{/* Image Links Collection */}
|
|
100
|
-
{imageLinks?.map(
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
108
|
+
{imageLinks?.map((link, index: number) => (
|
|
109
|
+
<div
|
|
110
|
+
key={index}
|
|
111
|
+
className="image-link w-[147px]"
|
|
112
|
+
{...link.fieldProps?.image}
|
|
113
|
+
>
|
|
114
|
+
<Link
|
|
115
|
+
variant="unstyled"
|
|
116
|
+
href={link.url}
|
|
117
|
+
target="_blank"
|
|
118
|
+
rel="noopener noreferrer"
|
|
119
|
+
>
|
|
120
|
+
<Image src={link.image} alt="icon-link" />
|
|
121
|
+
</Link>
|
|
122
|
+
</div>
|
|
123
|
+
))}
|
|
114
124
|
</div>
|
|
115
125
|
)}
|
|
116
126
|
{/* CTAs and Disclaimers */}
|
|
117
127
|
{(cta || secondaryCta) && (
|
|
118
128
|
<div className="flex w-full flex-col gap-4 xl:flex-row">
|
|
119
129
|
{cta && (
|
|
120
|
-
<div
|
|
130
|
+
<div
|
|
131
|
+
className="primary-cta w-full xl:w-auto xl:shrink-0"
|
|
132
|
+
{...fieldProps?.cta}
|
|
133
|
+
>
|
|
121
134
|
<Button
|
|
122
135
|
{...cta}
|
|
123
136
|
// Block-level `enableAnimation` takes precedence:
|
|
@@ -134,11 +147,15 @@ export const ImagePromoBar: React.FC<ImagePromoBarProps> = ({
|
|
|
134
147
|
renderCheckPlans={renderCheckPlans}
|
|
135
148
|
onModalButtonClick={onModalButtonClick}
|
|
136
149
|
onClick={(e: React.MouseEvent) => onClick?.(e, "primary")}
|
|
150
|
+
fieldProps={cta.fieldProps}
|
|
137
151
|
/>
|
|
138
152
|
</div>
|
|
139
153
|
)}
|
|
140
154
|
{secondaryCta && (
|
|
141
|
-
<div
|
|
155
|
+
<div
|
|
156
|
+
className="secondary-cta w-full xl:w-auto xl:shrink-0"
|
|
157
|
+
{...fieldProps?.secondaryCta}
|
|
158
|
+
>
|
|
142
159
|
<Button
|
|
143
160
|
{...secondaryCta}
|
|
144
161
|
// Block-level `enableAnimation` takes precedence:
|
|
@@ -158,6 +175,7 @@ export const ImagePromoBar: React.FC<ImagePromoBarProps> = ({
|
|
|
158
175
|
onClick={(e: React.MouseEvent) =>
|
|
159
176
|
onClick?.(e, "secondary")
|
|
160
177
|
}
|
|
178
|
+
fieldProps={secondaryCta.fieldProps}
|
|
161
179
|
/>
|
|
162
180
|
</div>
|
|
163
181
|
)}
|
|
@@ -209,7 +227,7 @@ export const ImagePromoBar: React.FC<ImagePromoBarProps> = ({
|
|
|
209
227
|
|
|
210
228
|
{disclaimer && (
|
|
211
229
|
<div className="mt-10 flex justify-center xl:mt-18">
|
|
212
|
-
<div>{disclaimer}</div>
|
|
230
|
+
<div {...fieldProps?.disclaimer}>{disclaimer}</div>
|
|
213
231
|
</div>
|
|
214
232
|
)}
|
|
215
233
|
</div>
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
VideoAnalyticsHandler,
|
|
5
5
|
VideoLinkProps,
|
|
6
6
|
} from "@shared/components/video-link";
|
|
7
|
+
import type { FieldProps } from "@shared/types/field-props";
|
|
7
8
|
import { CheckPlansProps } from "@shared/types/micro-components";
|
|
8
9
|
|
|
9
10
|
export type ImagePromoBarProps = {
|
|
@@ -22,7 +23,11 @@ export type ImagePromoBarProps = {
|
|
|
22
23
|
openDescriptionLinksOnANewTab: boolean;
|
|
23
24
|
checklist: string[];
|
|
24
25
|
disclaimer: React.ReactNode;
|
|
25
|
-
imageLinks: {
|
|
26
|
+
imageLinks: {
|
|
27
|
+
url: string;
|
|
28
|
+
image: string;
|
|
29
|
+
fieldProps?: FieldProps<"image">;
|
|
30
|
+
}[];
|
|
26
31
|
cta: any;
|
|
27
32
|
secondaryCta: any;
|
|
28
33
|
ctaDisclaimer: React.ReactNode;
|
|
@@ -34,4 +39,17 @@ export type ImagePromoBarProps = {
|
|
|
34
39
|
onModalButtonClick?: (id?: string) => void;
|
|
35
40
|
onClick?: (event: React.MouseEvent, source: "primary" | "secondary") => void;
|
|
36
41
|
renderCheckPlans?: (overrides?: CheckPlansProps) => React.ReactNode;
|
|
42
|
+
/** Consumer-supplied DOM attributes for the rendered content fields. */
|
|
43
|
+
fieldProps?: FieldProps<
|
|
44
|
+
| "brow"
|
|
45
|
+
| "title"
|
|
46
|
+
| "subTitle"
|
|
47
|
+
| "description"
|
|
48
|
+
| "checklist"
|
|
49
|
+
| "ctaDisclaimer"
|
|
50
|
+
| "cta"
|
|
51
|
+
| "secondaryCta"
|
|
52
|
+
| "disclaimer"
|
|
53
|
+
| "imageLinks"
|
|
54
|
+
>;
|
|
37
55
|
};
|
package/tailwind.config.cjs
CHANGED
|
@@ -483,7 +483,7 @@ module.exports = {
|
|
|
483
483
|
"font-family": theme("fontFamily.sans"),
|
|
484
484
|
"font-size": theme("fontSize.subheading5"), // 3.75rem/60px
|
|
485
485
|
"line-height": theme("lineHeight.15"), // 3.75rem/60px
|
|
486
|
-
"font-weight": "
|
|
486
|
+
"font-weight": "700",
|
|
487
487
|
},
|
|
488
488
|
".subheading6": {
|
|
489
489
|
"font-family": theme("fontFamily.sans"),
|