@windstream/react-shared-components 0.1.67 → 0.1.69
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/README.md +635 -635
- package/dist/contentful/index.d.ts +23 -0
- package/dist/contentful/index.esm.js +3 -3
- 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/core.d.ts +2 -2
- package/dist/index.d.ts +3 -3
- package/dist/index.esm.js +13 -5
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +13 -5
- package/dist/index.js.map +1 -1
- package/dist/next/index.esm.js +2 -2
- package/dist/next/index.esm.js.map +1 -1
- package/dist/next/index.js +2 -2
- package/dist/next/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/utils/index.esm.js +1 -1
- package/dist/utils/index.esm.js.map +1 -1
- package/dist/utils/index.js +1 -1
- package/dist/utils/index.js.map +1 -1
- package/package.json +185 -185
- package/src/components/accordion/Accordion.stories.tsx +230 -230
- package/src/components/accordion/index.tsx +70 -70
- package/src/components/accordion/types.ts +12 -12
- package/src/components/alert-card/AlertCard.stories.tsx +171 -171
- package/src/components/alert-card/index.tsx +41 -41
- package/src/components/alert-card/types.ts +13 -13
- package/src/components/brand-button/BrandButton.stories.tsx +223 -223
- package/src/components/brand-button/helpers.ts +35 -35
- package/src/components/brand-button/index.tsx +120 -120
- package/src/components/brand-button/types.ts +38 -38
- package/src/components/button/Button.stories.tsx +108 -108
- package/src/components/button/index.tsx +27 -27
- package/src/components/button/types.ts +14 -14
- package/src/components/call-button/CallButton.stories.tsx +324 -324
- package/src/components/call-button/index.tsx +106 -86
- package/src/components/call-button/types.ts +16 -11
- package/src/components/checkbox/Checkbox.stories.tsx +247 -247
- package/src/components/checkbox/index.tsx +197 -197
- package/src/components/checkbox/types.ts +27 -27
- package/src/components/checklist/Checklist.stories.tsx +150 -150
- package/src/components/checklist/index.tsx +61 -61
- package/src/components/checklist/types.ts +17 -17
- package/src/components/collapse/Collapse.stories.tsx +255 -255
- package/src/components/collapse/index.tsx +46 -46
- package/src/components/collapse/types.ts +6 -6
- package/src/components/divider/Divider.stories.tsx +205 -205
- package/src/components/divider/index.tsx +22 -22
- package/src/components/divider/type.ts +3 -3
- package/src/components/image/Image.stories.tsx +113 -113
- package/src/components/image/index.tsx +25 -25
- package/src/components/image/types.ts +40 -40
- package/src/components/input/Input.stories.tsx +325 -325
- package/src/components/input/index.tsx +177 -177
- package/src/components/input/types.ts +37 -37
- package/src/components/link/Link.stories.tsx +163 -163
- package/src/components/link/index.tsx +116 -109
- package/src/components/link/types.ts +25 -25
- package/src/components/list/List.stories.tsx +272 -272
- package/src/components/list/index.tsx +88 -88
- package/src/components/list/list-item/index.tsx +38 -38
- package/src/components/list/list-item/types.ts +13 -13
- package/src/components/list/types.ts +29 -29
- package/src/components/material-icon/MaterialIcon.stories.tsx +322 -322
- package/src/components/material-icon/constants.ts +99 -98
- package/src/components/material-icon/index.tsx +47 -47
- package/src/components/material-icon/types.ts +31 -31
- package/src/components/modal/Modal.stories.tsx +171 -171
- package/src/components/modal/index.tsx +164 -164
- package/src/components/modal/types.ts +24 -24
- package/src/components/next-image/index.tsx +72 -54
- package/src/components/next-image/types.ts +1 -1
- package/src/components/pagination/index.tsx +91 -91
- package/src/components/pagination/types.ts +6 -6
- package/src/components/radio-button/RadioButton.stories.tsx +307 -307
- package/src/components/radio-button/index.tsx +75 -75
- package/src/components/radio-button/types.ts +21 -21
- package/src/components/see-more/SeeMore.stories.tsx +181 -181
- package/src/components/see-more/index.tsx +44 -44
- package/src/components/see-more/types.ts +4 -4
- package/src/components/select/Select.stories.tsx +411 -411
- package/src/components/select/index.tsx +155 -155
- package/src/components/select/types.ts +36 -36
- package/src/components/select-plan-button/SelectPlanButton.stories.tsx +184 -184
- package/src/components/select-plan-button/index.tsx +63 -63
- package/src/components/select-plan-button/types.ts +17 -17
- package/src/components/skeleton/Skeleton.stories.tsx +179 -179
- package/src/components/skeleton/index.tsx +61 -61
- package/src/components/skeleton/types.ts +4 -4
- package/src/components/spinner/Spinner.stories.tsx +335 -335
- package/src/components/spinner/index.tsx +44 -44
- package/src/components/spinner/types.ts +5 -5
- package/src/components/text/Text.stories.tsx +321 -321
- package/src/components/text/index.tsx +25 -25
- package/src/components/text/types.ts +45 -45
- package/src/components/tooltip/Tooltip.stories.tsx +219 -219
- package/src/components/tooltip/index.tsx +74 -74
- package/src/components/tooltip/types.ts +7 -7
- package/src/components/view-cart-button/ViewCartButton.stories.tsx +252 -252
- package/src/components/view-cart-button/index.tsx +42 -42
- package/src/components/view-cart-button/types.ts +5 -5
- package/src/contentful/blocks/accordion/Accordion.stories.mocks.tsx +127 -127
- package/src/contentful/blocks/accordion/Accordion.stories.tsx +105 -105
- package/src/contentful/blocks/accordion/index.tsx +112 -112
- package/src/contentful/blocks/accordion/types.ts +34 -34
- package/src/contentful/blocks/address-input-banner/index.tsx +52 -52
- package/src/contentful/blocks/address-input-banner/types.ts +14 -14
- package/src/contentful/blocks/anchored-bottom-banner/index.tsx +70 -70
- package/src/contentful/blocks/anchored-bottom-banner/types.ts +10 -10
- package/src/contentful/blocks/blogs-grid/index.tsx +134 -134
- package/src/contentful/blocks/blogs-grid/types.ts +26 -26
- package/src/contentful/blocks/blogs-grid-base/index.tsx +119 -119
- package/src/contentful/blocks/blogs-grid-base/types.ts +35 -35
- package/src/contentful/blocks/breadcrumbs/types.ts +6 -6
- package/src/contentful/blocks/button/Button.stories.tsx +40 -40
- package/src/contentful/blocks/button/index.tsx +131 -131
- package/src/contentful/blocks/button/types.ts +39 -39
- package/src/contentful/blocks/callout/Callout.stories.tsx +23 -23
- package/src/contentful/blocks/callout/index.tsx +88 -88
- package/src/contentful/blocks/callout/types.ts +15 -15
- package/src/contentful/blocks/cards/Cards.stories.tsx +23 -23
- package/src/contentful/blocks/cards/blog-card/index.tsx +129 -129
- package/src/contentful/blocks/cards/blog-card/types.ts +34 -34
- package/src/contentful/blocks/cards/index.tsx +13 -13
- package/src/contentful/blocks/cards/product-card/index.tsx +251 -251
- package/src/contentful/blocks/cards/product-card/types.ts +28 -28
- package/src/contentful/blocks/cards/simple-card/index.tsx +89 -89
- package/src/contentful/blocks/cards/simple-card/types.ts +28 -28
- package/src/contentful/blocks/cards/testimonial-card/index.tsx +90 -90
- package/src/contentful/blocks/cards/testimonial-card/types.tsx +12 -12
- package/src/contentful/blocks/cards/types.ts +1 -1
- package/src/contentful/blocks/carousel/Carousel.stories.tsx +23 -23
- package/src/contentful/blocks/carousel/helper.tsx +440 -440
- package/src/contentful/blocks/carousel/index.tsx +85 -85
- package/src/contentful/blocks/carousel/types.ts +144 -144
- package/src/contentful/blocks/cart-retention-banner/index.tsx +105 -105
- package/src/contentful/blocks/cart-retention-banner/types.ts +11 -11
- package/src/contentful/blocks/comparison-table/index.tsx +27 -27
- package/src/contentful/blocks/comparison-table/types.ts +6 -6
- package/src/contentful/blocks/cookiebanner/index.tsx +146 -146
- package/src/contentful/blocks/cookiebanner/type.ts +7 -7
- package/src/contentful/blocks/cta-callout/CtaCallout.stories.tsx +46 -46
- package/src/contentful/blocks/cta-callout/index.tsx +71 -71
- package/src/contentful/blocks/cta-callout/types.ts +26 -26
- package/src/contentful/blocks/dynamic-tabs/index.tsx +204 -204
- package/src/contentful/blocks/dynamic-tabs/types.ts +21 -21
- package/src/contentful/blocks/email-input-block/index.tsx +117 -117
- package/src/contentful/blocks/email-input-block/types.ts +16 -16
- package/src/contentful/blocks/find-kinetic/FindKinetic.stories.tsx +23 -23
- package/src/contentful/blocks/find-kinetic/index.tsx +130 -130
- package/src/contentful/blocks/find-kinetic/types.ts +19 -19
- package/src/contentful/blocks/floating-banner/FloatingBanner.stories.tsx +34 -34
- package/src/contentful/blocks/floating-banner/index.tsx +97 -97
- package/src/contentful/blocks/floating-banner/types.ts +22 -22
- package/src/contentful/blocks/footer/Footer.stories.tsx +203 -203
- package/src/contentful/blocks/footer/index.tsx +91 -91
- package/src/contentful/blocks/footer/types.ts +13 -13
- package/src/contentful/blocks/image-promo-bar/ImagePromoBar.stories.tsx +23 -23
- package/src/contentful/blocks/image-promo-bar/helper.tsx +28 -28
- package/src/contentful/blocks/image-promo-bar/index.tsx +246 -246
- package/src/contentful/blocks/image-promo-bar/types.ts +44 -44
- package/src/contentful/blocks/image-promo-bar/vimeo-embed.tsx +93 -93
- package/src/contentful/blocks/image-promo-bar/youtube-embed.tsx +46 -46
- package/src/contentful/blocks/modal/constants.ts +53 -53
- package/src/contentful/blocks/modal/index.tsx +107 -107
- package/src/contentful/blocks/modal/types.ts +12 -12
- package/src/contentful/blocks/navigation/desktop-link-groups.tsx/index.tsx +124 -113
- package/src/contentful/blocks/navigation/index.tsx +462 -394
- package/src/contentful/blocks/navigation/mobile-link-groups.tsx/index.tsx +82 -82
- package/src/contentful/blocks/navigation/types.ts +64 -41
- package/src/contentful/blocks/primary-hero/PrimaryHero.stories.tsx +23 -23
- package/src/contentful/blocks/primary-hero/index.tsx +236 -236
- package/src/contentful/blocks/primary-hero/types.ts +37 -37
- package/src/contentful/blocks/search-block/index.tsx +90 -90
- package/src/contentful/blocks/search-block/types.ts +15 -15
- package/src/contentful/blocks/shape-background-wrapper/ShapeBackgroundWrapper.stories.tsx +26 -26
- package/src/contentful/blocks/shape-background-wrapper/index.tsx +124 -124
- package/src/contentful/blocks/shape-background-wrapper/types.ts +36 -36
- package/src/contentful/blocks/text/Text.stories.tsx +23 -23
- package/src/contentful/blocks/text/index.tsx +12 -12
- package/src/contentful/blocks/text/types.ts +1 -1
- package/src/contentful/index.ts +106 -106
- package/src/hooks/contentful/use-contentful-rich-text.tsx +310 -310
- package/src/hooks/contentful/use-processed-check-list.ts +63 -63
- package/src/hooks/use-body-scroll-lock.ts +34 -34
- package/src/hooks/use-carousel-swipe.ts +264 -264
- package/src/hooks/use-outside-click.ts +17 -17
- package/src/index.ts +101 -101
- package/src/next/index.ts +5 -5
- package/src/setupTests.ts +46 -46
- package/src/stories/DocsTemplate.tsx +24 -24
- package/src/styles/globals.css +343 -343
- package/src/types/global.d.ts +9 -9
- package/src/types/micro-components.ts +99 -99
- package/src/types/utm.ts +49 -49
- package/src/utils/contentful/to-document.ts +24 -24
- package/src/utils/cookie.ts +84 -84
- package/src/utils/cx.ts +49 -49
- package/src/utils/index.ts +38 -38
- package/src/utils/utm.ts +221 -221
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
export interface TypeCartRetentionBannerFields {
|
|
2
|
-
entryTitle?: string;
|
|
3
|
-
anchorId?: string;
|
|
4
|
-
mainHeading?: string;
|
|
5
|
-
description?: {
|
|
6
|
-
json: any;
|
|
7
|
-
};
|
|
8
|
-
cta?: { buttonLabel?: string; [key: string]: any };
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export type TypeCartRetentionBanner = TypeCartRetentionBannerFields;
|
|
1
|
+
export interface TypeCartRetentionBannerFields {
|
|
2
|
+
entryTitle?: string;
|
|
3
|
+
anchorId?: string;
|
|
4
|
+
mainHeading?: string;
|
|
5
|
+
description?: {
|
|
6
|
+
json: any;
|
|
7
|
+
};
|
|
8
|
+
cta?: { buttonLabel?: string; [key: string]: any };
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type TypeCartRetentionBanner = TypeCartRetentionBannerFields;
|
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { ComparisonTableProps } from "./types";
|
|
3
|
-
|
|
4
|
-
import { Text } from "@shared/components/text";
|
|
5
|
-
|
|
6
|
-
export const ComparisonTable: React.FC<ComparisonTableProps> = ({
|
|
7
|
-
title,
|
|
8
|
-
disclaimer,
|
|
9
|
-
table,
|
|
10
|
-
maxWidth = true,
|
|
11
|
-
}) => {
|
|
12
|
-
return (
|
|
13
|
-
<div className="component-container">
|
|
14
|
-
<div
|
|
15
|
-
className={`mx-5 mb-5 mt-8 lg:mt-10 ${maxWidth ? "max-w-120 xl:mx-auto" : ""}`}
|
|
16
|
-
>
|
|
17
|
-
<Text as="h2" className="heading2 lg:heading1 lg:text-center">
|
|
18
|
-
{title}
|
|
19
|
-
</Text>
|
|
20
|
-
<div className="comparison-table-container mt-20 xl:mt-10">{table}</div>
|
|
21
|
-
<Text as="div" className="micro mt-8 text-center">
|
|
22
|
-
{disclaimer}
|
|
23
|
-
</Text>
|
|
24
|
-
</div>
|
|
25
|
-
</div>
|
|
26
|
-
);
|
|
27
|
-
};
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { ComparisonTableProps } from "./types";
|
|
3
|
+
|
|
4
|
+
import { Text } from "@shared/components/text";
|
|
5
|
+
|
|
6
|
+
export const ComparisonTable: React.FC<ComparisonTableProps> = ({
|
|
7
|
+
title,
|
|
8
|
+
disclaimer,
|
|
9
|
+
table,
|
|
10
|
+
maxWidth = true,
|
|
11
|
+
}) => {
|
|
12
|
+
return (
|
|
13
|
+
<div className="component-container">
|
|
14
|
+
<div
|
|
15
|
+
className={`mx-5 mb-5 mt-8 lg:mt-10 ${maxWidth ? "max-w-120 xl:mx-auto" : ""}`}
|
|
16
|
+
>
|
|
17
|
+
<Text as="h2" className="heading2 lg:heading1 lg:text-center">
|
|
18
|
+
{title}
|
|
19
|
+
</Text>
|
|
20
|
+
<div className="comparison-table-container mt-20 xl:mt-10">{table}</div>
|
|
21
|
+
<Text as="div" className="micro mt-8 text-center">
|
|
22
|
+
{disclaimer}
|
|
23
|
+
</Text>
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
);
|
|
27
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export type ComparisonTableProps = {
|
|
2
|
-
title?: string;
|
|
3
|
-
disclaimer?: React.ReactNode;
|
|
4
|
-
table: React.ReactNode;
|
|
5
|
-
maxWidth?: boolean;
|
|
6
|
-
};
|
|
1
|
+
export type ComparisonTableProps = {
|
|
2
|
+
title?: string;
|
|
3
|
+
disclaimer?: React.ReactNode;
|
|
4
|
+
table: React.ReactNode;
|
|
5
|
+
maxWidth?: boolean;
|
|
6
|
+
};
|
|
@@ -1,146 +1,146 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { useEffect, useRef, useState } from "react";
|
|
4
|
-
import { getCookie, setCookie } from "../../../utils/cookie";
|
|
5
|
-
import { TypeComponentRichTextFields } from "./type";
|
|
6
|
-
|
|
7
|
-
import { MaterialIcon } from "@shared/components/material-icon";
|
|
8
|
-
import { Button } from "@shared/contentful/blocks/button";
|
|
9
|
-
|
|
10
|
-
export default function CookieBanner({
|
|
11
|
-
content,
|
|
12
|
-
}: {
|
|
13
|
-
content: TypeComponentRichTextFields;
|
|
14
|
-
}) {
|
|
15
|
-
let marginBottom = 3;
|
|
16
|
-
const [isBannerVisible, setIsBannerVisible] = useState(false);
|
|
17
|
-
const [isStickyFooterPresent, setStickyFooterPresent] = useState(false);
|
|
18
|
-
const bannerRef = useRef<HTMLDivElement>(null);
|
|
19
|
-
|
|
20
|
-
useEffect(() => {
|
|
21
|
-
const cookieBannerClosed = getCookie("cookieBannerClosed");
|
|
22
|
-
if (cookieBannerClosed === "true") {
|
|
23
|
-
setIsBannerVisible(false);
|
|
24
|
-
} else {
|
|
25
|
-
setIsBannerVisible(true);
|
|
26
|
-
}
|
|
27
|
-
}, []);
|
|
28
|
-
|
|
29
|
-
useEffect(() => {
|
|
30
|
-
const checkElementPresence = () => {
|
|
31
|
-
const element = document.getElementById("anchored-banner");
|
|
32
|
-
setStickyFooterPresent(!!element);
|
|
33
|
-
};
|
|
34
|
-
checkElementPresence();
|
|
35
|
-
}, []);
|
|
36
|
-
|
|
37
|
-
// Add body class and calculate height for chat positioning
|
|
38
|
-
useEffect(() => {
|
|
39
|
-
if (isBannerVisible) {
|
|
40
|
-
window?.document?.body?.classList.add("cookie-banner-visible");
|
|
41
|
-
} else {
|
|
42
|
-
window?.document?.body?.classList.remove("cookie-banner-visible");
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
return () => {
|
|
46
|
-
window?.document?.body?.classList.remove("cookie-banner-visible");
|
|
47
|
-
};
|
|
48
|
-
}, [isBannerVisible]);
|
|
49
|
-
|
|
50
|
-
// Calculate cookie banner height and set CSS custom property
|
|
51
|
-
useEffect(() => {
|
|
52
|
-
if (!isBannerVisible) return;
|
|
53
|
-
|
|
54
|
-
const calculateCookieBannerHeight = () => {
|
|
55
|
-
if (bannerRef.current) {
|
|
56
|
-
const rect = bannerRef.current.getBoundingClientRect();
|
|
57
|
-
const viewportHeight = window.innerHeight;
|
|
58
|
-
|
|
59
|
-
// Calculate how much space the banner occupies from the bottom of the viewport
|
|
60
|
-
// This is: viewport height - distance from top
|
|
61
|
-
const spaceFromBottom = viewportHeight - rect.top;
|
|
62
|
-
|
|
63
|
-
document.documentElement.style.setProperty(
|
|
64
|
-
"--cookie-banner-height",
|
|
65
|
-
`${spaceFromBottom}px`
|
|
66
|
-
);
|
|
67
|
-
}
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
// Calculate initially after render and after a short delay to ensure layout is complete
|
|
71
|
-
const timer1 = setTimeout(calculateCookieBannerHeight, 100);
|
|
72
|
-
const timer2 = setTimeout(calculateCookieBannerHeight, 300);
|
|
73
|
-
|
|
74
|
-
// Recalculate on window resize with debounce
|
|
75
|
-
let resizeTimeout: ReturnType<typeof setTimeout>;
|
|
76
|
-
const handleResize = () => {
|
|
77
|
-
clearTimeout(resizeTimeout);
|
|
78
|
-
resizeTimeout = setTimeout(calculateCookieBannerHeight, 150);
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
window.addEventListener("resize", handleResize);
|
|
82
|
-
|
|
83
|
-
return () => {
|
|
84
|
-
clearTimeout(timer1);
|
|
85
|
-
clearTimeout(timer2);
|
|
86
|
-
clearTimeout(resizeTimeout);
|
|
87
|
-
window.removeEventListener("resize", handleResize);
|
|
88
|
-
document.documentElement.style.removeProperty("--cookie-banner-height");
|
|
89
|
-
};
|
|
90
|
-
}, [isBannerVisible, isStickyFooterPresent]);
|
|
91
|
-
|
|
92
|
-
const handleClose = () => {
|
|
93
|
-
const expirationDate = new Date(Date.now() + 43200 * 60 * 1000);
|
|
94
|
-
setIsBannerVisible(false);
|
|
95
|
-
setCookie("cookieBannerClosed", true, {
|
|
96
|
-
expires: expirationDate,
|
|
97
|
-
});
|
|
98
|
-
};
|
|
99
|
-
if (isStickyFooterPresent) marginBottom = 14;
|
|
100
|
-
|
|
101
|
-
// Calculate margin values in pixels for inline styles
|
|
102
|
-
const marginBottomPx = marginBottom * 4; // Tailwind: 3 = 12px, 14 = 56px (each unit is 4px)
|
|
103
|
-
const marginLeftPx = 12; // mx-3 = 12px
|
|
104
|
-
|
|
105
|
-
if (!isBannerVisible) return null;
|
|
106
|
-
|
|
107
|
-
return (
|
|
108
|
-
<div
|
|
109
|
-
id="cookie-banner"
|
|
110
|
-
ref={bannerRef}
|
|
111
|
-
className="fixed z-[1000] max-w-[350px] rounded-xl bg-white py-5 pl-6 pr-12 text-black shadow-lg ring-1 ring-gray-900/10 md:max-w-[656px]"
|
|
112
|
-
style={{
|
|
113
|
-
// maxWidth: maxWidth,
|
|
114
|
-
bottom: "0px",
|
|
115
|
-
right: "20px",
|
|
116
|
-
marginBottom: `${marginBottomPx}px`,
|
|
117
|
-
marginLeft: `${marginLeftPx}px`,
|
|
118
|
-
}}
|
|
119
|
-
aria-label="Cookie usage notification"
|
|
120
|
-
>
|
|
121
|
-
{" "}
|
|
122
|
-
{
|
|
123
|
-
<Button
|
|
124
|
-
showButtonAs="unstyled"
|
|
125
|
-
buttonClassName="absolute right-2 top-3 mr-2 mt-0 h-6 w-6 bg-white"
|
|
126
|
-
onClick={handleClose}
|
|
127
|
-
>
|
|
128
|
-
<MaterialIcon name="close" />
|
|
129
|
-
</Button>
|
|
130
|
-
}
|
|
131
|
-
{/* {content ? (
|
|
132
|
-
<div className="my-4 mx-auto [&>a]:footnote">
|
|
133
|
-
{renderContentfulRichText(
|
|
134
|
-
toDocument(content.richText),
|
|
135
|
-
false,
|
|
136
|
-
"footnote",
|
|
137
|
-
"footnote",
|
|
138
|
-
)}
|
|
139
|
-
</div>
|
|
140
|
-
) : null} */}
|
|
141
|
-
{content.richText ? (
|
|
142
|
-
<div className="mx-auto my-4 [&>a]:footnote">{content.richText}</div>
|
|
143
|
-
) : null}
|
|
144
|
-
</div>
|
|
145
|
-
);
|
|
146
|
-
}
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useEffect, useRef, useState } from "react";
|
|
4
|
+
import { getCookie, setCookie } from "../../../utils/cookie";
|
|
5
|
+
import { TypeComponentRichTextFields } from "./type";
|
|
6
|
+
|
|
7
|
+
import { MaterialIcon } from "@shared/components/material-icon";
|
|
8
|
+
import { Button } from "@shared/contentful/blocks/button";
|
|
9
|
+
|
|
10
|
+
export default function CookieBanner({
|
|
11
|
+
content,
|
|
12
|
+
}: {
|
|
13
|
+
content: TypeComponentRichTextFields;
|
|
14
|
+
}) {
|
|
15
|
+
let marginBottom = 3;
|
|
16
|
+
const [isBannerVisible, setIsBannerVisible] = useState(false);
|
|
17
|
+
const [isStickyFooterPresent, setStickyFooterPresent] = useState(false);
|
|
18
|
+
const bannerRef = useRef<HTMLDivElement>(null);
|
|
19
|
+
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
const cookieBannerClosed = getCookie("cookieBannerClosed");
|
|
22
|
+
if (cookieBannerClosed === "true") {
|
|
23
|
+
setIsBannerVisible(false);
|
|
24
|
+
} else {
|
|
25
|
+
setIsBannerVisible(true);
|
|
26
|
+
}
|
|
27
|
+
}, []);
|
|
28
|
+
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
const checkElementPresence = () => {
|
|
31
|
+
const element = document.getElementById("anchored-banner");
|
|
32
|
+
setStickyFooterPresent(!!element);
|
|
33
|
+
};
|
|
34
|
+
checkElementPresence();
|
|
35
|
+
}, []);
|
|
36
|
+
|
|
37
|
+
// Add body class and calculate height for chat positioning
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
if (isBannerVisible) {
|
|
40
|
+
window?.document?.body?.classList.add("cookie-banner-visible");
|
|
41
|
+
} else {
|
|
42
|
+
window?.document?.body?.classList.remove("cookie-banner-visible");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return () => {
|
|
46
|
+
window?.document?.body?.classList.remove("cookie-banner-visible");
|
|
47
|
+
};
|
|
48
|
+
}, [isBannerVisible]);
|
|
49
|
+
|
|
50
|
+
// Calculate cookie banner height and set CSS custom property
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
if (!isBannerVisible) return;
|
|
53
|
+
|
|
54
|
+
const calculateCookieBannerHeight = () => {
|
|
55
|
+
if (bannerRef.current) {
|
|
56
|
+
const rect = bannerRef.current.getBoundingClientRect();
|
|
57
|
+
const viewportHeight = window.innerHeight;
|
|
58
|
+
|
|
59
|
+
// Calculate how much space the banner occupies from the bottom of the viewport
|
|
60
|
+
// This is: viewport height - distance from top
|
|
61
|
+
const spaceFromBottom = viewportHeight - rect.top;
|
|
62
|
+
|
|
63
|
+
document.documentElement.style.setProperty(
|
|
64
|
+
"--cookie-banner-height",
|
|
65
|
+
`${spaceFromBottom}px`
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
// Calculate initially after render and after a short delay to ensure layout is complete
|
|
71
|
+
const timer1 = setTimeout(calculateCookieBannerHeight, 100);
|
|
72
|
+
const timer2 = setTimeout(calculateCookieBannerHeight, 300);
|
|
73
|
+
|
|
74
|
+
// Recalculate on window resize with debounce
|
|
75
|
+
let resizeTimeout: ReturnType<typeof setTimeout>;
|
|
76
|
+
const handleResize = () => {
|
|
77
|
+
clearTimeout(resizeTimeout);
|
|
78
|
+
resizeTimeout = setTimeout(calculateCookieBannerHeight, 150);
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
window.addEventListener("resize", handleResize);
|
|
82
|
+
|
|
83
|
+
return () => {
|
|
84
|
+
clearTimeout(timer1);
|
|
85
|
+
clearTimeout(timer2);
|
|
86
|
+
clearTimeout(resizeTimeout);
|
|
87
|
+
window.removeEventListener("resize", handleResize);
|
|
88
|
+
document.documentElement.style.removeProperty("--cookie-banner-height");
|
|
89
|
+
};
|
|
90
|
+
}, [isBannerVisible, isStickyFooterPresent]);
|
|
91
|
+
|
|
92
|
+
const handleClose = () => {
|
|
93
|
+
const expirationDate = new Date(Date.now() + 43200 * 60 * 1000);
|
|
94
|
+
setIsBannerVisible(false);
|
|
95
|
+
setCookie("cookieBannerClosed", true, {
|
|
96
|
+
expires: expirationDate,
|
|
97
|
+
});
|
|
98
|
+
};
|
|
99
|
+
if (isStickyFooterPresent) marginBottom = 14;
|
|
100
|
+
|
|
101
|
+
// Calculate margin values in pixels for inline styles
|
|
102
|
+
const marginBottomPx = marginBottom * 4; // Tailwind: 3 = 12px, 14 = 56px (each unit is 4px)
|
|
103
|
+
const marginLeftPx = 12; // mx-3 = 12px
|
|
104
|
+
|
|
105
|
+
if (!isBannerVisible) return null;
|
|
106
|
+
|
|
107
|
+
return (
|
|
108
|
+
<div
|
|
109
|
+
id="cookie-banner"
|
|
110
|
+
ref={bannerRef}
|
|
111
|
+
className="fixed z-[1000] max-w-[350px] rounded-xl bg-white py-5 pl-6 pr-12 text-black shadow-lg ring-1 ring-gray-900/10 md:max-w-[656px]"
|
|
112
|
+
style={{
|
|
113
|
+
// maxWidth: maxWidth,
|
|
114
|
+
bottom: "0px",
|
|
115
|
+
right: "20px",
|
|
116
|
+
marginBottom: `${marginBottomPx}px`,
|
|
117
|
+
marginLeft: `${marginLeftPx}px`,
|
|
118
|
+
}}
|
|
119
|
+
aria-label="Cookie usage notification"
|
|
120
|
+
>
|
|
121
|
+
{" "}
|
|
122
|
+
{
|
|
123
|
+
<Button
|
|
124
|
+
showButtonAs="unstyled"
|
|
125
|
+
buttonClassName="absolute right-2 top-3 mr-2 mt-0 h-6 w-6 bg-white"
|
|
126
|
+
onClick={handleClose}
|
|
127
|
+
>
|
|
128
|
+
<MaterialIcon name="close" />
|
|
129
|
+
</Button>
|
|
130
|
+
}
|
|
131
|
+
{/* {content ? (
|
|
132
|
+
<div className="my-4 mx-auto [&>a]:footnote">
|
|
133
|
+
{renderContentfulRichText(
|
|
134
|
+
toDocument(content.richText),
|
|
135
|
+
false,
|
|
136
|
+
"footnote",
|
|
137
|
+
"footnote",
|
|
138
|
+
)}
|
|
139
|
+
</div>
|
|
140
|
+
) : null} */}
|
|
141
|
+
{content.richText ? (
|
|
142
|
+
<div className="mx-auto my-4 [&>a]:footnote">{content.richText}</div>
|
|
143
|
+
) : null}
|
|
144
|
+
</div>
|
|
145
|
+
);
|
|
146
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export interface TypeComponentRichTextFields {
|
|
2
|
-
entryName?: string;
|
|
3
|
-
anchorId?: string;
|
|
4
|
-
title?: string;
|
|
5
|
-
isTargetBlank?: boolean;
|
|
6
|
-
richText: React.ReactNode;
|
|
7
|
-
}
|
|
1
|
+
export interface TypeComponentRichTextFields {
|
|
2
|
+
entryName?: string;
|
|
3
|
+
anchorId?: string;
|
|
4
|
+
title?: string;
|
|
5
|
+
isTargetBlank?: boolean;
|
|
6
|
+
richText: React.ReactNode;
|
|
7
|
+
}
|
|
@@ -1,46 +1,46 @@
|
|
|
1
|
-
import { CtaCallout } from "./index";
|
|
2
|
-
|
|
3
|
-
import { DocsPage } from "@shared/stories/DocsTemplate";
|
|
4
|
-
import type { Meta, StoryObj } from "@storybook/react";
|
|
5
|
-
|
|
6
|
-
const meta: Meta<typeof CtaCallout> = {
|
|
7
|
-
title: "Contentful Blocks/CtaCallout",
|
|
8
|
-
component: CtaCallout,
|
|
9
|
-
tags: ["autodocs"],
|
|
10
|
-
parameters: {
|
|
11
|
-
layout: "centered",
|
|
12
|
-
docs: {
|
|
13
|
-
page: DocsPage,
|
|
14
|
-
description: {
|
|
15
|
-
component:
|
|
16
|
-
"Contentful CTA callout block with title, description, and optional button.",
|
|
17
|
-
},
|
|
18
|
-
},
|
|
19
|
-
},
|
|
20
|
-
args: {
|
|
21
|
-
title: "Call to Action",
|
|
22
|
-
subTitle: "Subtitle",
|
|
23
|
-
description: "Description text.",
|
|
24
|
-
background: "white",
|
|
25
|
-
button: {
|
|
26
|
-
showButtonAs: "solid",
|
|
27
|
-
buttonVariant: "primary_brand",
|
|
28
|
-
buttonLabel: "Learn More",
|
|
29
|
-
buttonPrefix: "",
|
|
30
|
-
badge: "",
|
|
31
|
-
badgeIcon: "",
|
|
32
|
-
buttonIcon: "",
|
|
33
|
-
buttonIconPosition: "left",
|
|
34
|
-
href: "",
|
|
35
|
-
target: "_self",
|
|
36
|
-
anchorId: "",
|
|
37
|
-
preserveQueryParameters: false,
|
|
38
|
-
clickToOpen: "",
|
|
39
|
-
tabmodalNameToOpen: "",
|
|
40
|
-
preDefinedFunctionExecution: "",
|
|
41
|
-
},
|
|
42
|
-
},
|
|
43
|
-
};
|
|
44
|
-
export default meta;
|
|
45
|
-
type Story = StoryObj<typeof meta>;
|
|
46
|
-
export const Default: Story = {};
|
|
1
|
+
import { CtaCallout } from "./index";
|
|
2
|
+
|
|
3
|
+
import { DocsPage } from "@shared/stories/DocsTemplate";
|
|
4
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
5
|
+
|
|
6
|
+
const meta: Meta<typeof CtaCallout> = {
|
|
7
|
+
title: "Contentful Blocks/CtaCallout",
|
|
8
|
+
component: CtaCallout,
|
|
9
|
+
tags: ["autodocs"],
|
|
10
|
+
parameters: {
|
|
11
|
+
layout: "centered",
|
|
12
|
+
docs: {
|
|
13
|
+
page: DocsPage,
|
|
14
|
+
description: {
|
|
15
|
+
component:
|
|
16
|
+
"Contentful CTA callout block with title, description, and optional button.",
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
args: {
|
|
21
|
+
title: "Call to Action",
|
|
22
|
+
subTitle: "Subtitle",
|
|
23
|
+
description: "Description text.",
|
|
24
|
+
background: "white",
|
|
25
|
+
button: {
|
|
26
|
+
showButtonAs: "solid",
|
|
27
|
+
buttonVariant: "primary_brand",
|
|
28
|
+
buttonLabel: "Learn More",
|
|
29
|
+
buttonPrefix: "",
|
|
30
|
+
badge: "",
|
|
31
|
+
badgeIcon: "",
|
|
32
|
+
buttonIcon: "",
|
|
33
|
+
buttonIconPosition: "left",
|
|
34
|
+
href: "",
|
|
35
|
+
target: "_self",
|
|
36
|
+
anchorId: "",
|
|
37
|
+
preserveQueryParameters: false,
|
|
38
|
+
clickToOpen: "",
|
|
39
|
+
tabmodalNameToOpen: "",
|
|
40
|
+
preDefinedFunctionExecution: "",
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
export default meta;
|
|
45
|
+
type Story = StoryObj<typeof meta>;
|
|
46
|
+
export const Default: Story = {};
|
|
@@ -1,71 +1,71 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { CtaCalloutProps, ThemeKey } from "./types";
|
|
3
|
-
|
|
4
|
-
import { Text } from "@shared/components/text";
|
|
5
|
-
import { Button } from "@shared/contentful/blocks/button";
|
|
6
|
-
|
|
7
|
-
export const CtaCallout: React.FC<CtaCalloutProps> = ({
|
|
8
|
-
title,
|
|
9
|
-
background = "white",
|
|
10
|
-
button,
|
|
11
|
-
color,
|
|
12
|
-
contentAlignment,
|
|
13
|
-
description,
|
|
14
|
-
descriptionAlignment,
|
|
15
|
-
enableHeading,
|
|
16
|
-
subTitle,
|
|
17
|
-
maxWidth = true,
|
|
18
|
-
onModalButtonClick,
|
|
19
|
-
renderCheckPlans,
|
|
20
|
-
}) => {
|
|
21
|
-
const bgColorClasses: Record<ThemeKey, string> = {
|
|
22
|
-
navy: "bg-bg-fill-inverse",
|
|
23
|
-
green: "bg-bg-fill-success",
|
|
24
|
-
blue: "bg-bg-fill-brand",
|
|
25
|
-
purple: "bg-bg-fill-brand-tertiary",
|
|
26
|
-
yellow: "bg-bg-fill-brand-accent",
|
|
27
|
-
white: "bg-bg",
|
|
28
|
-
};
|
|
29
|
-
return (
|
|
30
|
-
<div
|
|
31
|
-
className={`${bgColorClasses[background]} component-container px-5 py-16 lg:px-13 lg:py-24`}
|
|
32
|
-
>
|
|
33
|
-
<div
|
|
34
|
-
className={`${maxWidth ? "mx-auto max-w-120" : ""} color-${color} flex flex-col ${color == "dark" ? "text-text" : "text-text-inverse"}`}
|
|
35
|
-
>
|
|
36
|
-
{title && (
|
|
37
|
-
<Text
|
|
38
|
-
as={enableHeading ? "h1" : "h2"}
|
|
39
|
-
className={`heading2 lg:heading1 text-${contentAlignment}`}
|
|
40
|
-
>
|
|
41
|
-
{title}
|
|
42
|
-
</Text>
|
|
43
|
-
)}
|
|
44
|
-
{subTitle && (
|
|
45
|
-
<Text
|
|
46
|
-
as="h3"
|
|
47
|
-
className={`subheading3 pt-2 lg:subheading1 lg:pt-3 text-${contentAlignment}`}
|
|
48
|
-
>
|
|
49
|
-
{subTitle}
|
|
50
|
-
</Text>
|
|
51
|
-
)}
|
|
52
|
-
{description && (
|
|
53
|
-
<div
|
|
54
|
-
className={`pt-2 text-body1 lg:pt-3 text-${descriptionAlignment}`}
|
|
55
|
-
>
|
|
56
|
-
{description}
|
|
57
|
-
</div>
|
|
58
|
-
)}
|
|
59
|
-
<div className="flex justify-center pt-5 lg:pt-14">
|
|
60
|
-
<Button
|
|
61
|
-
{...button}
|
|
62
|
-
renderCheckPlans={renderCheckPlans}
|
|
63
|
-
onModalButtonClick={onModalButtonClick}
|
|
64
|
-
size={{ base: "large" }}
|
|
65
|
-
/>
|
|
66
|
-
</div>
|
|
67
|
-
</div>
|
|
68
|
-
</div>
|
|
69
|
-
);
|
|
70
|
-
};
|
|
71
|
-
export default CtaCallout;
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { CtaCalloutProps, ThemeKey } from "./types";
|
|
3
|
+
|
|
4
|
+
import { Text } from "@shared/components/text";
|
|
5
|
+
import { Button } from "@shared/contentful/blocks/button";
|
|
6
|
+
|
|
7
|
+
export const CtaCallout: React.FC<CtaCalloutProps> = ({
|
|
8
|
+
title,
|
|
9
|
+
background = "white",
|
|
10
|
+
button,
|
|
11
|
+
color,
|
|
12
|
+
contentAlignment,
|
|
13
|
+
description,
|
|
14
|
+
descriptionAlignment,
|
|
15
|
+
enableHeading,
|
|
16
|
+
subTitle,
|
|
17
|
+
maxWidth = true,
|
|
18
|
+
onModalButtonClick,
|
|
19
|
+
renderCheckPlans,
|
|
20
|
+
}) => {
|
|
21
|
+
const bgColorClasses: Record<ThemeKey, string> = {
|
|
22
|
+
navy: "bg-bg-fill-inverse",
|
|
23
|
+
green: "bg-bg-fill-success",
|
|
24
|
+
blue: "bg-bg-fill-brand",
|
|
25
|
+
purple: "bg-bg-fill-brand-tertiary",
|
|
26
|
+
yellow: "bg-bg-fill-brand-accent",
|
|
27
|
+
white: "bg-bg",
|
|
28
|
+
};
|
|
29
|
+
return (
|
|
30
|
+
<div
|
|
31
|
+
className={`${bgColorClasses[background]} component-container px-5 py-16 lg:px-13 lg:py-24`}
|
|
32
|
+
>
|
|
33
|
+
<div
|
|
34
|
+
className={`${maxWidth ? "mx-auto max-w-120" : ""} color-${color} flex flex-col ${color == "dark" ? "text-text" : "text-text-inverse"}`}
|
|
35
|
+
>
|
|
36
|
+
{title && (
|
|
37
|
+
<Text
|
|
38
|
+
as={enableHeading ? "h1" : "h2"}
|
|
39
|
+
className={`heading2 lg:heading1 text-${contentAlignment}`}
|
|
40
|
+
>
|
|
41
|
+
{title}
|
|
42
|
+
</Text>
|
|
43
|
+
)}
|
|
44
|
+
{subTitle && (
|
|
45
|
+
<Text
|
|
46
|
+
as="h3"
|
|
47
|
+
className={`subheading3 pt-2 lg:subheading1 lg:pt-3 text-${contentAlignment}`}
|
|
48
|
+
>
|
|
49
|
+
{subTitle}
|
|
50
|
+
</Text>
|
|
51
|
+
)}
|
|
52
|
+
{description && (
|
|
53
|
+
<div
|
|
54
|
+
className={`pt-2 text-body1 lg:pt-3 text-${descriptionAlignment}`}
|
|
55
|
+
>
|
|
56
|
+
{description}
|
|
57
|
+
</div>
|
|
58
|
+
)}
|
|
59
|
+
<div className="flex justify-center pt-5 lg:pt-14">
|
|
60
|
+
<Button
|
|
61
|
+
{...button}
|
|
62
|
+
renderCheckPlans={renderCheckPlans}
|
|
63
|
+
onModalButtonClick={onModalButtonClick}
|
|
64
|
+
size={{ base: "large" }}
|
|
65
|
+
/>
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
);
|
|
70
|
+
};
|
|
71
|
+
export default CtaCallout;
|