@windstream/react-shared-components 0.2.51 → 0.2.53
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 +24 -2
- package/dist/contentful/index.esm.js +15 -3
- package/dist/contentful/index.esm.js.map +1 -1
- package/dist/contentful/index.js +15 -3
- package/dist/contentful/index.js.map +1 -1
- package/dist/core.d.ts +3 -3
- package/dist/index.d.ts +2 -2
- package/dist/index.esm.js +18 -6
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +18 -6
- package/dist/index.js.map +1 -1
- package/dist/next/index.esm.js +16 -2
- package/dist/next/index.esm.js.map +1 -1
- package/dist/next/index.js +16 -2
- package/dist/next/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/src/contentful/blocks/award-marquee/AwardMarquee.stories.tsx +50 -0
- package/src/contentful/blocks/award-marquee/index.test.tsx +86 -0
- package/src/contentful/blocks/award-marquee/index.tsx +110 -0
- package/src/contentful/blocks/award-marquee/types.ts +19 -0
- package/src/contentful/blocks/navigation/index.test.tsx +34 -0
- package/src/contentful/blocks/navigation/index.tsx +33 -4
- package/src/contentful/blocks/navigation/types.ts +3 -0
- package/src/contentful/index.ts +8 -0
- package/tailwind.config.cjs +7 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import {
|
|
3
|
+
AwardMarqueeBackground,
|
|
4
|
+
AwardMarqueeItem,
|
|
5
|
+
AwardMarqueeProps,
|
|
6
|
+
AwardMarqueeSpeed,
|
|
7
|
+
} from "./types";
|
|
8
|
+
|
|
9
|
+
import { cx } from "@shared/utils";
|
|
10
|
+
|
|
11
|
+
const DURATION_IN_SECONDS: Record<AwardMarqueeSpeed, number> = {
|
|
12
|
+
slow: 30,
|
|
13
|
+
medium: 20,
|
|
14
|
+
fast: 10,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const BACKGROUND_CLASS: Record<AwardMarqueeBackground, string> = {
|
|
18
|
+
cream: "bg-[#FFFEEF]",
|
|
19
|
+
gray90: "bg-[#FBFBFC]",
|
|
20
|
+
white: "bg-white",
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const renderAwardItem = (
|
|
24
|
+
{ iconUrl, iconAlt, title, description }: AwardMarqueeItem,
|
|
25
|
+
key: React.Key
|
|
26
|
+
) => (
|
|
27
|
+
<div
|
|
28
|
+
key={key}
|
|
29
|
+
className="ml-4 flex w-auto flex-shrink-0 items-center whitespace-nowrap"
|
|
30
|
+
>
|
|
31
|
+
<img src={iconUrl} alt={iconAlt ?? ""} width={200} />
|
|
32
|
+
<div className="w-[250px] text-center">
|
|
33
|
+
<p className="flex justify-center text-center text-body3 font-bold">
|
|
34
|
+
{title}
|
|
35
|
+
</p>
|
|
36
|
+
<p className="flex flex-wrap whitespace-normal text-center text-footnote">
|
|
37
|
+
{description}
|
|
38
|
+
</p>
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
export const AwardMarquee: React.FC<AwardMarqueeProps> = ({
|
|
44
|
+
anchorId = "Award-Marquee",
|
|
45
|
+
items = [],
|
|
46
|
+
speed = "medium",
|
|
47
|
+
scroll = false,
|
|
48
|
+
backgroundColor = "white",
|
|
49
|
+
className,
|
|
50
|
+
}) => {
|
|
51
|
+
const scrollAvailable = items.length > 3;
|
|
52
|
+
const shouldDuplicate = items.length < 8;
|
|
53
|
+
const duration = DURATION_IN_SECONDS[speed];
|
|
54
|
+
|
|
55
|
+
const animatedMarquee = () => {
|
|
56
|
+
const marqueeItems = shouldDuplicate ? [...items, ...items] : items;
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<div
|
|
60
|
+
className={cx(
|
|
61
|
+
"flex items-center overflow-x-hidden",
|
|
62
|
+
BACKGROUND_CLASS[backgroundColor]
|
|
63
|
+
)}
|
|
64
|
+
>
|
|
65
|
+
<div
|
|
66
|
+
className="flex animate-award-marquee gap-4 px-2"
|
|
67
|
+
style={
|
|
68
|
+
{
|
|
69
|
+
"--award-marquee-duration": `${duration}s`,
|
|
70
|
+
} as React.CSSProperties
|
|
71
|
+
}
|
|
72
|
+
>
|
|
73
|
+
{marqueeItems.map((item, index) => renderAwardItem(item, index))}
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
);
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const staticMarquee = (
|
|
80
|
+
<div className="flex flex-nowrap items-center justify-around overflow-clip">
|
|
81
|
+
{items.map((item, index) => renderAwardItem(item, index))}
|
|
82
|
+
</div>
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
// No scroll: always static at every breakpoint.
|
|
86
|
+
if (!scroll)
|
|
87
|
+
return (
|
|
88
|
+
<div id={anchorId} className={className}>
|
|
89
|
+
{staticMarquee}
|
|
90
|
+
</div>
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
// Scroll with >3 items: animate at every breakpoint.
|
|
94
|
+
if (scrollAvailable)
|
|
95
|
+
return (
|
|
96
|
+
<div id={anchorId} className={className}>
|
|
97
|
+
{animatedMarquee()}
|
|
98
|
+
</div>
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
// Scroll with <=3 items: animate on mobile, static on >= sm (CSS-driven).
|
|
102
|
+
return (
|
|
103
|
+
<div id={anchorId} className={className}>
|
|
104
|
+
<div className="sm:hidden">{animatedMarquee()}</div>
|
|
105
|
+
<div className="hidden sm:block">{staticMarquee}</div>
|
|
106
|
+
</div>
|
|
107
|
+
);
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
export default AwardMarquee;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type AwardMarqueeSpeed = "fast" | "medium" | "slow";
|
|
2
|
+
|
|
3
|
+
export type AwardMarqueeBackground = "cream" | "gray90" | "white";
|
|
4
|
+
|
|
5
|
+
export type AwardMarqueeItem = {
|
|
6
|
+
iconUrl: string;
|
|
7
|
+
iconAlt?: string;
|
|
8
|
+
title?: string;
|
|
9
|
+
description?: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type AwardMarqueeProps = {
|
|
13
|
+
anchorId?: string;
|
|
14
|
+
items: AwardMarqueeItem[];
|
|
15
|
+
speed?: AwardMarqueeSpeed;
|
|
16
|
+
scroll?: boolean;
|
|
17
|
+
backgroundColor?: AwardMarqueeBackground;
|
|
18
|
+
className?: string;
|
|
19
|
+
};
|
|
@@ -149,6 +149,40 @@ const mobileMenuProps: NavigationProps = {
|
|
|
149
149
|
describe("Navigation", () => {
|
|
150
150
|
beforeEach(() => jest.clearAllMocks());
|
|
151
151
|
|
|
152
|
+
describe("enableHamburgerNavigationOnDesktop (desktop hamburger A/B variant)", () => {
|
|
153
|
+
it("keeps current behavior when the flag is off", () => {
|
|
154
|
+
const { container } = render(<Navigation {...defaultProps} />);
|
|
155
|
+
|
|
156
|
+
expect(
|
|
157
|
+
container.querySelector(".mobile-nav-section")?.className
|
|
158
|
+
).toContain("lg:hidden");
|
|
159
|
+
expect(
|
|
160
|
+
container.querySelector(".desktop-nav-section")?.className
|
|
161
|
+
).toContain("lg:block");
|
|
162
|
+
expect(
|
|
163
|
+
container.querySelector(".utility-container")?.className
|
|
164
|
+
).toContain("lg:block");
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it("shows the hamburger and hides desktop + utility sections when enabled", () => {
|
|
168
|
+
const { container } = render(
|
|
169
|
+
<Navigation {...defaultProps} enableHamburgerNavigationOnDesktop />
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
// mobile section no longer hidden on desktop
|
|
173
|
+
expect(
|
|
174
|
+
container.querySelector(".mobile-nav-section")?.className
|
|
175
|
+
).not.toContain("lg:hidden");
|
|
176
|
+
// desktop + utility hidden at all widths (no lg:block to re-show them)
|
|
177
|
+
expect(
|
|
178
|
+
container.querySelector(".desktop-nav-section")?.className
|
|
179
|
+
).not.toContain("lg:block");
|
|
180
|
+
expect(
|
|
181
|
+
container.querySelector(".utility-container")?.className
|
|
182
|
+
).not.toContain("lg:block");
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
|
|
152
186
|
describe("Basic rendering", () => {
|
|
153
187
|
it("renders nav with utility and main nav sections", () => {
|
|
154
188
|
const { container } = render(<Navigation {...defaultProps} />);
|
|
@@ -42,6 +42,7 @@ export const Navigation: React.FC<NavigationProps> = props => {
|
|
|
42
42
|
showCallButton = true,
|
|
43
43
|
showCallNowCtaInMainNav = false,
|
|
44
44
|
showMobileSliderMenu = true,
|
|
45
|
+
enableHamburgerNavigationOnDesktop = false,
|
|
45
46
|
callNowCtaIcon,
|
|
46
47
|
navigationBackgroundColor,
|
|
47
48
|
navigationLinkColor,
|
|
@@ -106,11 +107,28 @@ export const Navigation: React.FC<NavigationProps> = props => {
|
|
|
106
107
|
(supportNavigationLinks?.length ?? 0) > 0 ||
|
|
107
108
|
(accountNavigationLinks?.length ?? 0) > 0;
|
|
108
109
|
|
|
110
|
+
// A/B variant: when enabled, the hamburger (mobile section) shows on desktop
|
|
111
|
+
// and the inline desktop nav is hidden. Off = current behavior.
|
|
112
|
+
const mobileNavResponsiveClass = enableHamburgerNavigationOnDesktop
|
|
113
|
+
? undefined // visible at all widths, including desktop
|
|
114
|
+
: "lg:hidden"; // default: mobile/tablet only
|
|
115
|
+
const desktopNavResponsiveClass = enableHamburgerNavigationOnDesktop
|
|
116
|
+
? "hidden" // hidden at all widths
|
|
117
|
+
: "hidden lg:block"; // default: desktop only
|
|
118
|
+
const utilityNavResponsiveClass = enableHamburgerNavigationOnDesktop
|
|
119
|
+
? "hidden" // match mobile/tablet: hidden on desktop too
|
|
120
|
+
: "hidden lg:block"; // default: desktop only
|
|
121
|
+
|
|
109
122
|
return (
|
|
110
123
|
<div className="component-container">
|
|
111
124
|
<nav className={`menu-container z-[1000]`}>
|
|
112
125
|
{displayUtilityNavigation ? (
|
|
113
|
-
<div
|
|
126
|
+
<div
|
|
127
|
+
className={cx(
|
|
128
|
+
"utility-container lg:border-b lg:px-2",
|
|
129
|
+
utilityNavResponsiveClass
|
|
130
|
+
)}
|
|
131
|
+
>
|
|
114
132
|
<div className="mx-auto flex max-w-120 justify-between">
|
|
115
133
|
<ul className="flex gap-5" aria-label="Utility Navigation">
|
|
116
134
|
{utilityNavigationLinks?.map((links, index) => {
|
|
@@ -181,7 +199,12 @@ export const Navigation: React.FC<NavigationProps> = props => {
|
|
|
181
199
|
)}
|
|
182
200
|
aria-label="Main Navigation"
|
|
183
201
|
>
|
|
184
|
-
<div
|
|
202
|
+
<div
|
|
203
|
+
className={cx(
|
|
204
|
+
"mobile-nav-section flex h-14 items-center justify-between border border-b px-5 py-[10px]",
|
|
205
|
+
mobileNavResponsiveClass
|
|
206
|
+
)}
|
|
207
|
+
>
|
|
185
208
|
<div>
|
|
186
209
|
{logoUrl ? (
|
|
187
210
|
<Link href="/" className="flex">
|
|
@@ -230,7 +253,12 @@ export const Navigation: React.FC<NavigationProps> = props => {
|
|
|
230
253
|
</div>
|
|
231
254
|
</div>
|
|
232
255
|
|
|
233
|
-
<div
|
|
256
|
+
<div
|
|
257
|
+
className={cx(
|
|
258
|
+
"desktop-nav-section lg:border-b lg:px-2",
|
|
259
|
+
desktopNavResponsiveClass
|
|
260
|
+
)}
|
|
261
|
+
>
|
|
234
262
|
<div className="mx-auto flex h-14 max-w-120 items-center justify-between">
|
|
235
263
|
<div className="flex h-full">
|
|
236
264
|
{logoUrl ? (
|
|
@@ -553,7 +581,8 @@ const DesktopSearchInput = (props: {
|
|
|
553
581
|
height={32}
|
|
554
582
|
alt="Search icon"
|
|
555
583
|
role="button"
|
|
556
|
-
onClick={redirectToSearchResults}
|
|
584
|
+
onClick={redirectToSearchResults}
|
|
585
|
+
/>
|
|
557
586
|
{/* Input */}
|
|
558
587
|
<Input
|
|
559
588
|
ref={searchInputRef}
|
|
@@ -66,6 +66,9 @@ export interface Navigation {
|
|
|
66
66
|
* (the one next to the cart icon and hamburger). The drawer
|
|
67
67
|
* still renders the CallButton. Defaults to false. */
|
|
68
68
|
hideMobileCallButton?: boolean;
|
|
69
|
+
/** When true, renders the mobile hamburger navigation in the desktop
|
|
70
|
+
* viewport and hides the inline desktop nav. Defaults to false. */
|
|
71
|
+
enableHamburgerNavigationOnDesktop?: boolean;
|
|
69
72
|
}
|
|
70
73
|
|
|
71
74
|
export type NavigationProps = Navigation;
|
package/src/contentful/index.ts
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
export { Accordion } from "./blocks/accordion";
|
|
3
3
|
export type { AccordionProps } from "./blocks/accordion/types";
|
|
4
4
|
|
|
5
|
+
export { AwardMarquee } from "./blocks/award-marquee";
|
|
6
|
+
export type {
|
|
7
|
+
AwardMarqueeProps,
|
|
8
|
+
AwardMarqueeItem,
|
|
9
|
+
AwardMarqueeSpeed,
|
|
10
|
+
AwardMarqueeBackground,
|
|
11
|
+
} from "./blocks/award-marquee/types";
|
|
12
|
+
|
|
5
13
|
export { Callout } from "./blocks/callout";
|
|
6
14
|
export type { CalloutProps } from "./blocks/callout/types";
|
|
7
15
|
|
package/tailwind.config.cjs
CHANGED
|
@@ -378,10 +378,17 @@ module.exports = {
|
|
|
378
378
|
"70%": { transform: "scale(1.6)", opacity: "0" },
|
|
379
379
|
"100%": { transform: "scale(1.6)", opacity: "0" },
|
|
380
380
|
},
|
|
381
|
+
awardMarqueeScroll: {
|
|
382
|
+
from: { transform: "translateX(0)" },
|
|
383
|
+
to: { transform: "translateX(-100%)" },
|
|
384
|
+
},
|
|
381
385
|
},
|
|
382
386
|
animation: {
|
|
383
387
|
blink: "blink 1.2s ease-in-out infinite",
|
|
384
388
|
"pulse-dot": "pulseDot 1.6s ease-out infinite",
|
|
389
|
+
// Duration is supplied per-instance via the --award-marquee-duration var.
|
|
390
|
+
"award-marquee":
|
|
391
|
+
"awardMarqueeScroll var(--award-marquee-duration, 20s) linear infinite",
|
|
385
392
|
},
|
|
386
393
|
},
|
|
387
394
|
screens: {
|