@windstream/react-shared-components 0.2.32 → 0.2.34
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 +3 -6
- package/dist/contentful/index.esm.js +2 -2
- package/dist/contentful/index.esm.js.map +1 -1
- package/dist/contentful/index.js +3 -3
- package/dist/contentful/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/src/components/video-link/contentful-video-embed.test.tsx +76 -0
- package/src/components/video-link/contentful-video-embed.tsx +42 -0
- package/src/components/video-link/index.test.tsx +227 -0
- package/src/components/video-link/index.tsx +142 -0
- package/src/components/video-link/types.ts +28 -0
- package/src/contentful/blocks/blogs-grid-base/index.tsx +6 -1
- package/src/contentful/blocks/blogs-grid-base/types.ts +1 -0
- package/src/contentful/blocks/button/index.test.tsx +52 -0
- package/src/contentful/blocks/button/index.tsx +8 -3
- package/src/contentful/blocks/image-promo-bar/index.test.tsx +15 -198
- package/src/contentful/blocks/image-promo-bar/index.tsx +8 -92
- package/src/contentful/blocks/image-promo-bar/types.ts +1 -17
- package/src/contentful/blocks/primary-hero/index.tsx +18 -18
- /package/src/{contentful/blocks/image-promo-bar → components/video-link}/helper.test.tsx +0 -0
- /package/src/{contentful/blocks/image-promo-bar → components/video-link}/helper.tsx +0 -0
- /package/src/{contentful/blocks/image-promo-bar → components/video-link}/vimeo-embed.test.tsx +0 -0
- /package/src/{contentful/blocks/image-promo-bar → components/video-link}/vimeo-embed.tsx +0 -0
- /package/src/{contentful/blocks/image-promo-bar → components/video-link}/youtube-embed.test.tsx +0 -0
- /package/src/{contentful/blocks/image-promo-bar → components/video-link}/youtube-embed.tsx +0 -0
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { VideoLink } from "./index";
|
|
3
|
+
|
|
4
|
+
import { fireEvent, render, screen } from "@testing-library/react";
|
|
5
|
+
|
|
6
|
+
jest.mock("./contentful-video-embed", () => ({
|
|
7
|
+
ContentfulVideoEmbed: ({ videoAssetUrl, autoplay }: any) => (
|
|
8
|
+
<div
|
|
9
|
+
data-testid="contentful-video-embed"
|
|
10
|
+
data-video-asset-url={videoAssetUrl}
|
|
11
|
+
data-autoplay={autoplay}
|
|
12
|
+
/>
|
|
13
|
+
),
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
jest.mock("./vimeo-embed", () => ({
|
|
17
|
+
VimeoEmbed: ({ link, autoplay }: any) => (
|
|
18
|
+
<div data-testid="vimeo-embed" data-link={link} data-autoplay={autoplay} />
|
|
19
|
+
),
|
|
20
|
+
}));
|
|
21
|
+
|
|
22
|
+
jest.mock("./youtube-embed", () => ({
|
|
23
|
+
YoutubeEmbed: ({ link, autoplay }: any) => (
|
|
24
|
+
<div
|
|
25
|
+
data-testid="youtube-embed"
|
|
26
|
+
data-link={link}
|
|
27
|
+
data-autoplay={autoplay}
|
|
28
|
+
/>
|
|
29
|
+
),
|
|
30
|
+
}));
|
|
31
|
+
|
|
32
|
+
jest.mock("./helper", () => ({
|
|
33
|
+
PlayButton: ({ isHovered }: any) => (
|
|
34
|
+
<div data-testid="play-button" data-hovered={isHovered} />
|
|
35
|
+
),
|
|
36
|
+
}));
|
|
37
|
+
|
|
38
|
+
jest.mock("@shared/components/next-image", () => ({
|
|
39
|
+
NextImage: ({ src, alt }: any) => (
|
|
40
|
+
<img data-testid="next-image" src={src} alt={alt} />
|
|
41
|
+
),
|
|
42
|
+
}));
|
|
43
|
+
|
|
44
|
+
jest.mock("@shared/utils", () => ({
|
|
45
|
+
cx: (...args: any[]) => args.filter(Boolean).join(" "),
|
|
46
|
+
}));
|
|
47
|
+
|
|
48
|
+
const clickVideoSection = (container: HTMLElement) => {
|
|
49
|
+
fireEvent.click(container.querySelector(".video-section")!);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
describe("VideoLink", () => {
|
|
53
|
+
it("renders nothing when videoLink is undefined", () => {
|
|
54
|
+
const { container } = render(<VideoLink />);
|
|
55
|
+
expect(container.innerHTML).toBe("");
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("renders nothing when videoLink has neither link nor videoAssetUrl", () => {
|
|
59
|
+
const { container } = render(
|
|
60
|
+
<VideoLink videoLink={{ image: "thumb.jpg" }} />
|
|
61
|
+
);
|
|
62
|
+
expect(container.innerHTML).toBe("");
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("renders nothing when videoSourceType is 'contentful' but videoAssetUrl is missing", () => {
|
|
66
|
+
const { container } = render(
|
|
67
|
+
<VideoLink
|
|
68
|
+
videoLink={{ videoSourceType: "contentful", image: "thumb.jpg" }}
|
|
69
|
+
/>
|
|
70
|
+
);
|
|
71
|
+
expect(container.innerHTML).toBe("");
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("renders preview image and play button when videoLink has link and image", () => {
|
|
75
|
+
render(
|
|
76
|
+
<VideoLink
|
|
77
|
+
videoLink={{
|
|
78
|
+
link: "https://youtube.com/watch?v=123",
|
|
79
|
+
image: "thumb.jpg",
|
|
80
|
+
}}
|
|
81
|
+
/>
|
|
82
|
+
);
|
|
83
|
+
expect(screen.getByTestId("play-button")).toBeInTheDocument();
|
|
84
|
+
expect(screen.getByAltText("Video preview")).toBeInTheDocument();
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it("does not render preview image when videoLink.image is missing", () => {
|
|
88
|
+
render(
|
|
89
|
+
<VideoLink videoLink={{ link: "https://youtube.com/watch?v=123" }} />
|
|
90
|
+
);
|
|
91
|
+
expect(screen.queryByAltText("Video preview")).not.toBeInTheDocument();
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it("shows youtube embed inline on click for a youtube link", () => {
|
|
95
|
+
const { container } = render(
|
|
96
|
+
<VideoLink
|
|
97
|
+
videoLink={{
|
|
98
|
+
link: "https://youtube.com/watch?v=123",
|
|
99
|
+
image: "thumb.jpg",
|
|
100
|
+
}}
|
|
101
|
+
/>
|
|
102
|
+
);
|
|
103
|
+
clickVideoSection(container);
|
|
104
|
+
expect(screen.getByTestId("youtube-embed")).toBeInTheDocument();
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it("shows vimeo embed inline on click for a vimeo link", () => {
|
|
108
|
+
const { container } = render(
|
|
109
|
+
<VideoLink
|
|
110
|
+
videoLink={{ link: "https://vimeo.com/123", image: "thumb.jpg" }}
|
|
111
|
+
/>
|
|
112
|
+
);
|
|
113
|
+
clickVideoSection(container);
|
|
114
|
+
expect(screen.getByTestId("vimeo-embed")).toBeInTheDocument();
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it("shows the contentful embed when videoSourceType is 'contentful'", () => {
|
|
118
|
+
const { container } = render(
|
|
119
|
+
<VideoLink
|
|
120
|
+
videoLink={{
|
|
121
|
+
videoSourceType: "contentful",
|
|
122
|
+
videoAssetUrl: "https://videos.ctfassets.net/space/asset/sample.mp4",
|
|
123
|
+
image: "thumb.jpg",
|
|
124
|
+
}}
|
|
125
|
+
/>
|
|
126
|
+
);
|
|
127
|
+
clickVideoSection(container);
|
|
128
|
+
expect(screen.getByTestId("contentful-video-embed")).toHaveAttribute(
|
|
129
|
+
"data-video-asset-url",
|
|
130
|
+
"https://videos.ctfassets.net/space/asset/sample.mp4"
|
|
131
|
+
);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it("shows popup overlay when videoPopup is true and playing", () => {
|
|
135
|
+
const { container } = render(
|
|
136
|
+
<VideoLink
|
|
137
|
+
videoLink={{
|
|
138
|
+
link: "https://youtube.com/watch?v=123",
|
|
139
|
+
image: "thumb.jpg",
|
|
140
|
+
videoPopup: true,
|
|
141
|
+
}}
|
|
142
|
+
/>
|
|
143
|
+
);
|
|
144
|
+
clickVideoSection(container);
|
|
145
|
+
expect(container.querySelector(".fixed")).toBeInTheDocument();
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it("does not render inline embed when videoPopup is true", () => {
|
|
149
|
+
const { container } = render(
|
|
150
|
+
<VideoLink
|
|
151
|
+
videoLink={{
|
|
152
|
+
link: "https://youtube.com/watch?v=123",
|
|
153
|
+
image: "thumb.jpg",
|
|
154
|
+
videoPopup: true,
|
|
155
|
+
}}
|
|
156
|
+
/>
|
|
157
|
+
);
|
|
158
|
+
clickVideoSection(container);
|
|
159
|
+
expect(container.querySelector(".fixed")).toBeInTheDocument();
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it("renders inline (not popup) when videoPopup is not provided", () => {
|
|
163
|
+
const { container } = render(
|
|
164
|
+
<VideoLink
|
|
165
|
+
videoLink={{
|
|
166
|
+
link: "https://youtube.com/watch?v=123",
|
|
167
|
+
image: "thumb.jpg",
|
|
168
|
+
}}
|
|
169
|
+
/>
|
|
170
|
+
);
|
|
171
|
+
clickVideoSection(container);
|
|
172
|
+
expect(container.querySelector(".fixed")).not.toBeInTheDocument();
|
|
173
|
+
expect(screen.getByTestId("youtube-embed")).toBeInTheDocument();
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it("closes popup on overlay click", () => {
|
|
177
|
+
const { container } = render(
|
|
178
|
+
<VideoLink
|
|
179
|
+
videoLink={{
|
|
180
|
+
link: "https://youtube.com/watch?v=123",
|
|
181
|
+
image: "thumb.jpg",
|
|
182
|
+
videoPopup: true,
|
|
183
|
+
}}
|
|
184
|
+
/>
|
|
185
|
+
);
|
|
186
|
+
clickVideoSection(container);
|
|
187
|
+
fireEvent.click(container.querySelector(".fixed")!);
|
|
188
|
+
expect(container.querySelector(".fixed")).not.toBeInTheDocument();
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it("does not close popup when clicking inside the popup content", () => {
|
|
192
|
+
const { container } = render(
|
|
193
|
+
<VideoLink
|
|
194
|
+
videoLink={{
|
|
195
|
+
link: "https://youtube.com/watch?v=123",
|
|
196
|
+
image: "thumb.jpg",
|
|
197
|
+
videoPopup: true,
|
|
198
|
+
}}
|
|
199
|
+
/>
|
|
200
|
+
);
|
|
201
|
+
clickVideoSection(container);
|
|
202
|
+
fireEvent.click(container.querySelector(".max-w-6xl")!);
|
|
203
|
+
expect(container.querySelector(".fixed")).toBeInTheDocument();
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it("handles hover state on the video section", () => {
|
|
207
|
+
const { container } = render(
|
|
208
|
+
<VideoLink
|
|
209
|
+
videoLink={{
|
|
210
|
+
link: "https://youtube.com/watch?v=123",
|
|
211
|
+
image: "thumb.jpg",
|
|
212
|
+
}}
|
|
213
|
+
/>
|
|
214
|
+
);
|
|
215
|
+
const videoSection = container.querySelector(".video-section")!;
|
|
216
|
+
fireEvent.mouseEnter(videoSection);
|
|
217
|
+
expect(screen.getByTestId("play-button")).toHaveAttribute(
|
|
218
|
+
"data-hovered",
|
|
219
|
+
"true"
|
|
220
|
+
);
|
|
221
|
+
fireEvent.mouseLeave(videoSection);
|
|
222
|
+
expect(screen.getByTestId("play-button")).toHaveAttribute(
|
|
223
|
+
"data-hovered",
|
|
224
|
+
"false"
|
|
225
|
+
);
|
|
226
|
+
});
|
|
227
|
+
});
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import React, { useState } from "react";
|
|
2
|
+
import { ContentfulVideoEmbed } from "./contentful-video-embed";
|
|
3
|
+
import { PlayButton } from "./helper";
|
|
4
|
+
import { VideoLinkComponentProps } from "./types";
|
|
5
|
+
import { VimeoEmbed } from "./vimeo-embed";
|
|
6
|
+
import { YoutubeEmbed } from "./youtube-embed";
|
|
7
|
+
|
|
8
|
+
import { NextImage } from "@shared/components/next-image";
|
|
9
|
+
import { cx } from "@shared/utils";
|
|
10
|
+
|
|
11
|
+
// Owns all video state (active/hover) so ImagePromoBar stays state-free for video.
|
|
12
|
+
export const VideoLink: React.FC<VideoLinkComponentProps> = ({
|
|
13
|
+
videoLink,
|
|
14
|
+
imageWidth = 660,
|
|
15
|
+
imageHeight = 660,
|
|
16
|
+
useOriginalImageAspectRatio = false,
|
|
17
|
+
}) => {
|
|
18
|
+
const [activeVideo, setActiveVideo] = useState("");
|
|
19
|
+
const [isHovered, setIsHovered] = useState(false);
|
|
20
|
+
|
|
21
|
+
const videoSourceType = videoLink?.videoSourceType;
|
|
22
|
+
const videoHref =
|
|
23
|
+
(videoSourceType === "contentful"
|
|
24
|
+
? videoLink?.videoAssetUrl
|
|
25
|
+
: videoLink?.link) ?? "";
|
|
26
|
+
|
|
27
|
+
if (!videoHref) {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const videoPopup = videoLink?.videoPopup ?? false;
|
|
32
|
+
const isPlaying = Boolean(activeVideo && activeVideo === videoHref);
|
|
33
|
+
|
|
34
|
+
const handlePlayClick = () => {
|
|
35
|
+
setActiveVideo(videoHref);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const handleCloseVideo = (e: React.MouseEvent) => {
|
|
39
|
+
e.stopPropagation();
|
|
40
|
+
setActiveVideo("");
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const embedSelector = () => {
|
|
44
|
+
if (videoSourceType === "contentful") {
|
|
45
|
+
return (
|
|
46
|
+
<ContentfulVideoEmbed
|
|
47
|
+
videoAssetUrl={videoLink?.videoAssetUrl}
|
|
48
|
+
videoAssetType={videoLink?.videoAssetType}
|
|
49
|
+
image={videoLink?.image}
|
|
50
|
+
title={videoLink?.title}
|
|
51
|
+
description={videoLink?.description}
|
|
52
|
+
autoplay={isPlaying}
|
|
53
|
+
/>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
if (videoHref.includes("vimeo")) {
|
|
57
|
+
return <VimeoEmbed link={videoHref} autoplay={isPlaying} />;
|
|
58
|
+
}
|
|
59
|
+
return <YoutubeEmbed link={videoHref} autoplay={isPlaying} />;
|
|
60
|
+
};
|
|
61
|
+
const contentVideo = embedSelector();
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<>
|
|
65
|
+
<div
|
|
66
|
+
className={cx(
|
|
67
|
+
"video-section relative w-full cursor-pointer rounded-image transition-all duration-300 lg:w-[486px]",
|
|
68
|
+
!isPlaying && "hover:ring-2 hover:ring-border-focus"
|
|
69
|
+
)}
|
|
70
|
+
onClick={handlePlayClick}
|
|
71
|
+
onMouseEnter={() => setIsHovered(true)}
|
|
72
|
+
onMouseLeave={() => setIsHovered(false)}
|
|
73
|
+
>
|
|
74
|
+
{/* Preview Image & Play Button */}
|
|
75
|
+
<div
|
|
76
|
+
className={cx(
|
|
77
|
+
isPlaying && !videoPopup && "hidden",
|
|
78
|
+
isPlaying && !videoPopup ? "opacity-0" : "opacity-100",
|
|
79
|
+
"relative w-full transition-opacity duration-300"
|
|
80
|
+
)}
|
|
81
|
+
>
|
|
82
|
+
{videoLink?.image && (
|
|
83
|
+
<NextImage
|
|
84
|
+
src={videoLink.image}
|
|
85
|
+
alt="Video preview"
|
|
86
|
+
width={imageWidth || 660}
|
|
87
|
+
height={imageHeight || 660}
|
|
88
|
+
sizes="(max-width: 430px) 100vw, (max-width: 834px) 100vw, 50vw"
|
|
89
|
+
className="h-auto w-full rounded-image"
|
|
90
|
+
/>
|
|
91
|
+
)}
|
|
92
|
+
<div className="absolute inset-0 flex items-center justify-center">
|
|
93
|
+
<PlayButton isHovered={isHovered} />
|
|
94
|
+
</div>
|
|
95
|
+
</div>
|
|
96
|
+
|
|
97
|
+
{/* Inline Player (if not popup) */}
|
|
98
|
+
{!videoPopup && isPlaying && (
|
|
99
|
+
<div
|
|
100
|
+
className={cx(
|
|
101
|
+
"aspect-[16/9] w-full overflow-hidden rounded-image transition-opacity duration-300",
|
|
102
|
+
isPlaying ? "opacity-100" : "opacity-0",
|
|
103
|
+
!useOriginalImageAspectRatio &&
|
|
104
|
+
"xl:aspect-square xl:max-h-[486px] xl:w-[486px]"
|
|
105
|
+
)}
|
|
106
|
+
>
|
|
107
|
+
{contentVideo}
|
|
108
|
+
</div>
|
|
109
|
+
)}
|
|
110
|
+
</div>
|
|
111
|
+
|
|
112
|
+
{/* Video Popup Overlay */}
|
|
113
|
+
{videoPopup && isPlaying && (
|
|
114
|
+
<div
|
|
115
|
+
className="fixed inset-0 top-20 z-[100] flex items-center justify-center bg-black/80 p-4 transition-all duration-300"
|
|
116
|
+
onClick={handleCloseVideo}
|
|
117
|
+
>
|
|
118
|
+
<div
|
|
119
|
+
className="max-w-6xl aspect-video w-full overflow-hidden rounded-3xl bg-black shadow-2xl md:w-4/6"
|
|
120
|
+
onClick={e => e.stopPropagation()}
|
|
121
|
+
>
|
|
122
|
+
{contentVideo}
|
|
123
|
+
</div>
|
|
124
|
+
</div>
|
|
125
|
+
)}
|
|
126
|
+
</>
|
|
127
|
+
);
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
export default VideoLink;
|
|
131
|
+
|
|
132
|
+
export { ContentfulVideoEmbed } from "./contentful-video-embed";
|
|
133
|
+
export { PlayButton } from "./helper";
|
|
134
|
+
export { VimeoEmbed } from "./vimeo-embed";
|
|
135
|
+
export { YoutubeEmbed } from "./youtube-embed";
|
|
136
|
+
|
|
137
|
+
export type {
|
|
138
|
+
PlayButtonProps,
|
|
139
|
+
VideoEmbedProps,
|
|
140
|
+
VideoLinkComponentProps,
|
|
141
|
+
VideoLinkProps,
|
|
142
|
+
} from "./types";
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export type VideoLinkProps = {
|
|
2
|
+
image?: string;
|
|
3
|
+
videoPopup?: boolean;
|
|
4
|
+
link?: string;
|
|
5
|
+
videoSourceType?: "youtube" | "contentful";
|
|
6
|
+
videoAssetUrl?: string;
|
|
7
|
+
videoAssetType?: string;
|
|
8
|
+
title?: string;
|
|
9
|
+
description?: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type VideoEmbedProps = {
|
|
13
|
+
containerClassName?: string;
|
|
14
|
+
autoplay?: boolean;
|
|
15
|
+
debug?: boolean;
|
|
16
|
+
} & VideoLinkProps;
|
|
17
|
+
|
|
18
|
+
export type VideoLinkComponentProps = {
|
|
19
|
+
videoLink?: VideoLinkProps;
|
|
20
|
+
imageWidth?: number;
|
|
21
|
+
imageHeight?: number;
|
|
22
|
+
useOriginalImageAspectRatio?: boolean;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export type PlayButtonProps = {
|
|
26
|
+
isHovered?: boolean;
|
|
27
|
+
containerClassName?: string;
|
|
28
|
+
};
|
|
@@ -7,6 +7,7 @@ import { BlogCard } from "@shared/contentful/blocks/cards/blog-card";
|
|
|
7
7
|
|
|
8
8
|
export function BlogGridBase({
|
|
9
9
|
title = "recent articles",
|
|
10
|
+
description,
|
|
10
11
|
paginatedArticles,
|
|
11
12
|
totalArticles,
|
|
12
13
|
currentPage,
|
|
@@ -37,10 +38,14 @@ export function BlogGridBase({
|
|
|
37
38
|
>
|
|
38
39
|
{/* Controls bar */}
|
|
39
40
|
<div className="mx-auto flex max-w-[1200px] flex-wrap items-center justify-between gap-3 px-6 pb-6">
|
|
41
|
+
{description && (
|
|
42
|
+
<Text as="p" className="body1 mb-4 w-full text-center md:mb-6">
|
|
43
|
+
{description}
|
|
44
|
+
</Text>
|
|
45
|
+
)}
|
|
40
46
|
<Text className="w-full text-center text-heading5 font-black lowercase text-text-secondary md:w-auto md:text-left">
|
|
41
47
|
{title}
|
|
42
48
|
</Text>
|
|
43
|
-
|
|
44
49
|
{categoryOptions.length > 0 && (
|
|
45
50
|
<div className="flex w-full flex-col-reverse gap-3 md:w-auto md:flex-row md:items-center md:gap-6">
|
|
46
51
|
{/* Showing count */}
|
|
@@ -283,6 +283,58 @@ describe("Button", () => {
|
|
|
283
283
|
fireEvent.click(screen.getByTestId("link"));
|
|
284
284
|
expect(onModalButtonClick).toHaveBeenCalledWith("tab1");
|
|
285
285
|
});
|
|
286
|
+
|
|
287
|
+
it("prevents anchor navigation and opens modal when href is present and clickToOpen is 'modal'", () => {
|
|
288
|
+
const onModalButtonClick = jest.fn();
|
|
289
|
+
render(
|
|
290
|
+
<Button
|
|
291
|
+
buttonLabel="Open"
|
|
292
|
+
href="https://example.com"
|
|
293
|
+
clickToOpen="modal"
|
|
294
|
+
tabmodalNameToOpen="my-modal"
|
|
295
|
+
onModalButtonClick={onModalButtonClick}
|
|
296
|
+
/>
|
|
297
|
+
);
|
|
298
|
+
// fireEvent.click returns false when a handler called preventDefault().
|
|
299
|
+
const notCancelled = fireEvent.click(screen.getByTestId("brand-button"));
|
|
300
|
+
expect(notCancelled).toBe(false);
|
|
301
|
+
expect(onModalButtonClick).toHaveBeenCalledWith("my-modal");
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
it("prevents anchor navigation when href is present and clickToOpen is 'clickId'", () => {
|
|
305
|
+
const scrollIntoView = jest.fn();
|
|
306
|
+
const targetEl = document.createElement("div");
|
|
307
|
+
targetEl.scrollIntoView = scrollIntoView;
|
|
308
|
+
const getElementByIdSpy = jest
|
|
309
|
+
.spyOn(document, "getElementById")
|
|
310
|
+
.mockReturnValue(targetEl);
|
|
311
|
+
render(
|
|
312
|
+
<Button
|
|
313
|
+
buttonLabel="Scroll"
|
|
314
|
+
href="https://example.com"
|
|
315
|
+
clickToOpen="clickId"
|
|
316
|
+
idNameToOpen="section-1"
|
|
317
|
+
/>
|
|
318
|
+
);
|
|
319
|
+
const notCancelled = fireEvent.click(screen.getByTestId("brand-button"));
|
|
320
|
+
expect(notCancelled).toBe(false);
|
|
321
|
+
expect(scrollIntoView).toHaveBeenCalled();
|
|
322
|
+
getElementByIdSpy.mockRestore();
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
it("does not prevent anchor navigation for a normal href link", () => {
|
|
326
|
+
const onModalButtonClick = jest.fn();
|
|
327
|
+
render(
|
|
328
|
+
<Button
|
|
329
|
+
buttonLabel="Go"
|
|
330
|
+
href="https://example.com"
|
|
331
|
+
onModalButtonClick={onModalButtonClick}
|
|
332
|
+
/>
|
|
333
|
+
);
|
|
334
|
+
const notCancelled = fireEvent.click(screen.getByTestId("brand-button"));
|
|
335
|
+
expect(notCancelled).toBe(true);
|
|
336
|
+
expect(onModalButtonClick).not.toHaveBeenCalled();
|
|
337
|
+
});
|
|
286
338
|
});
|
|
287
339
|
|
|
288
340
|
describe("preserveQueryParameters", () => {
|
|
@@ -84,22 +84,27 @@ export const Button: React.FC<ButtonProps> = props => {
|
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
function handleModalClick(
|
|
87
|
+
function handleModalClick(
|
|
88
|
+
event?: React.MouseEvent<HTMLAnchorElement | HTMLButtonElement>
|
|
89
|
+
) {
|
|
88
90
|
if (clickToOpen === "modal") {
|
|
91
|
+
// Prevent the anchor's href from navigating so the CTA only opens the modal.
|
|
92
|
+
event?.preventDefault();
|
|
89
93
|
onModalButtonClick?.(tabmodalNameToOpen);
|
|
90
94
|
}
|
|
91
95
|
if (clickToOpen === "clickId") {
|
|
96
|
+
event?.preventDefault();
|
|
92
97
|
handleClickId();
|
|
93
98
|
}
|
|
94
99
|
}
|
|
95
100
|
|
|
96
101
|
function linkClick(event: React.MouseEvent<HTMLAnchorElement>) {
|
|
97
|
-
handleModalClick();
|
|
102
|
+
handleModalClick(event);
|
|
98
103
|
onClick?.(event);
|
|
99
104
|
}
|
|
100
105
|
|
|
101
106
|
function buttonClick(event: React.MouseEvent<HTMLButtonElement>) {
|
|
102
|
-
handleModalClick();
|
|
107
|
+
handleModalClick(event);
|
|
103
108
|
onClick?.(event);
|
|
104
109
|
}
|
|
105
110
|
|