@windstream/react-shared-components 0.1.92 → 0.1.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.
- package/README.md +635 -635
- package/dist/contentful/index.d.ts +1 -0
- 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/core.d.ts +5 -5
- package/dist/index.d.ts +2 -2
- package/dist/index.esm.js +1 -1
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/utils/index.esm.js +1 -1
- package/dist/utils/index.js +1 -1
- package/package.json +191 -185
- package/src/components/accordion/Accordion.stories.tsx +230 -230
- package/src/components/accordion/index.test.tsx +270 -0
- 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.test.tsx +152 -0
- package/src/components/alert-card/index.tsx +41 -41
- package/src/components/alert-card/types.ts +13 -13
- package/src/components/animation-wrapper/index.test.tsx +424 -0
- package/src/components/animation-wrapper/index.tsx +129 -129
- package/src/components/animation-wrapper/types.ts +11 -11
- 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.test.tsx +292 -0
- 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.test.tsx +91 -0
- 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.test.tsx +260 -0
- package/src/components/call-button/index.tsx +106 -106
- package/src/components/call-button/types.ts +16 -16
- package/src/components/checkbox/Checkbox.stories.tsx +247 -247
- package/src/components/checkbox/index.test.tsx +252 -0
- 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.test.tsx +231 -0
- package/src/components/checklist/index.tsx +96 -61
- package/src/components/checklist/types.ts +23 -17
- package/src/components/collapse/Collapse.stories.tsx +255 -255
- package/src/components/collapse/index.test.tsx +277 -0
- package/src/components/collapse/index.tsx +47 -46
- package/src/components/collapse/types.ts +6 -6
- package/src/components/divider/Divider.stories.tsx +205 -205
- package/src/components/divider/index.test.tsx +53 -0
- 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.test.tsx +174 -0
- 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.test.tsx +348 -0
- 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.test.tsx +199 -0
- package/src/components/link/index.tsx +116 -116
- package/src/components/link/types.ts +25 -25
- package/src/components/list/List.stories.tsx +272 -272
- package/src/components/list/index.test.tsx +166 -0
- 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 -99
- package/src/components/material-icon/index.test.tsx +130 -0
- 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.test.tsx +310 -0
- package/src/components/modal/index.tsx +164 -164
- package/src/components/modal/types.ts +24 -24
- package/src/components/next-image/index.test.tsx +406 -0
- package/src/components/next-image/index.tsx +74 -74
- package/src/components/next-image/types.ts +1 -1
- package/src/components/pagination/index.test.tsx +521 -0
- 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.test.tsx +151 -0
- 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.test.tsx +96 -0
- 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.test.tsx +256 -0
- 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.test.tsx +173 -0
- 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.test.tsx +74 -0
- 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.test.tsx +76 -0
- 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.test.tsx +65 -0
- 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.test.tsx +50 -0
- 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.test.tsx +57 -0
- 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 +128 -128
- package/src/contentful/blocks/accordion/Accordion.stories.tsx +98 -98
- package/src/contentful/blocks/accordion/index.test.tsx +218 -0
- package/src/contentful/blocks/accordion/index.tsx +114 -112
- package/src/contentful/blocks/accordion/types.ts +34 -34
- package/src/contentful/blocks/address-input-banner/index.test.tsx +132 -0
- 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.test.tsx +287 -0
- package/src/contentful/blocks/anchored-bottom-banner/index.tsx +181 -181
- package/src/contentful/blocks/anchored-bottom-banner/types.ts +13 -13
- package/src/contentful/blocks/blogs-grid/BlogGrid.stories.mocks.tsx +144 -144
- package/src/contentful/blocks/blogs-grid/BlogGrid.stories.tsx +157 -156
- package/src/contentful/blocks/blogs-grid/index.test.tsx +355 -0
- 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.test.tsx +274 -0
- package/src/contentful/blocks/blogs-grid-base/index.tsx +119 -119
- package/src/contentful/blocks/blogs-grid-base/types.ts +36 -36
- package/src/contentful/blocks/breadcrumbs/BreadcrumbNavigation.stories.tsx +147 -147
- package/src/contentful/blocks/breadcrumbs/index.test.tsx +281 -0
- package/src/contentful/blocks/breadcrumbs/index.tsx +95 -95
- package/src/contentful/blocks/breadcrumbs/types.ts +8 -8
- package/src/contentful/blocks/button/Button.stories.tsx +40 -40
- package/src/contentful/blocks/button/index.test.tsx +339 -0
- 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.test.tsx +539 -0
- package/src/contentful/blocks/callout/index.tsx +277 -277
- package/src/contentful/blocks/callout/types.ts +78 -78
- package/src/contentful/blocks/cards/Cards.stories.tsx +23 -23
- package/src/contentful/blocks/cards/blog-card/index.test.tsx +218 -0
- 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/floating-image-card/index.test.tsx +201 -0
- package/src/contentful/blocks/cards/floating-image-card/index.tsx +119 -119
- package/src/contentful/blocks/cards/floating-image-card/types.ts +30 -30
- package/src/contentful/blocks/cards/full-image-card/index.test.tsx +216 -0
- package/src/contentful/blocks/cards/full-image-card/index.tsx +130 -130
- package/src/contentful/blocks/cards/full-image-card/types.ts +29 -29
- package/src/contentful/blocks/cards/index.test.tsx +39 -0
- package/src/contentful/blocks/cards/index.tsx +13 -13
- package/src/contentful/blocks/cards/product-card/index.test.tsx +263 -0
- 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.test.tsx +364 -0
- package/src/contentful/blocks/cards/simple-card/index.tsx +325 -325
- package/src/contentful/blocks/cards/simple-card/types.ts +71 -71
- package/src/contentful/blocks/cards/testimonial-card/index.test.tsx +180 -0
- 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.test.tsx +539 -0
- package/src/contentful/blocks/carousel/helper.tsx +494 -494
- package/src/contentful/blocks/carousel/index.test.tsx +308 -0
- package/src/contentful/blocks/carousel/index.tsx +87 -87
- package/src/contentful/blocks/carousel/types.test.ts +16 -0
- package/src/contentful/blocks/carousel/types.ts +145 -145
- package/src/contentful/blocks/cart-retention-banner/index.test.tsx +409 -0
- package/src/contentful/blocks/cart-retention-banner/index.tsx +109 -109
- package/src/contentful/blocks/cart-retention-banner/types.ts +11 -11
- package/src/contentful/blocks/comparison-table/index.test.tsx +114 -0
- package/src/contentful/blocks/comparison-table/index.tsx +29 -29
- package/src/contentful/blocks/comparison-table/types.ts +6 -6
- package/src/contentful/blocks/cookiebanner/index.test.tsx +277 -0
- 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.test.tsx +244 -0
- package/src/contentful/blocks/cta-callout/index.tsx +73 -73
- package/src/contentful/blocks/cta-callout/types.ts +26 -26
- package/src/contentful/blocks/dynamic-tabs/index.test.tsx +240 -0
- 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.test.tsx +213 -0
- package/src/contentful/blocks/email-input-block/index.tsx +121 -116
- 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.test.tsx +269 -0
- package/src/contentful/blocks/find-kinetic/index.tsx +138 -130
- package/src/contentful/blocks/find-kinetic/types.ts +20 -19
- package/src/contentful/blocks/floating-banner/FloatingBanner.stories.tsx +34 -34
- package/src/contentful/blocks/floating-banner/index.test.tsx +246 -0
- 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 +317 -317
- package/src/contentful/blocks/footer/index.test.tsx +302 -0
- 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.test.tsx +61 -0
- package/src/contentful/blocks/image-promo-bar/helper.tsx +28 -28
- package/src/contentful/blocks/image-promo-bar/index.test.tsx +467 -0
- 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.test.tsx +142 -0
- package/src/contentful/blocks/image-promo-bar/vimeo-embed.tsx +93 -93
- package/src/contentful/blocks/image-promo-bar/youtube-embed.test.tsx +104 -0
- 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.test.tsx +209 -0
- package/src/contentful/blocks/modal/index.tsx +108 -108
- package/src/contentful/blocks/modal/types.ts +12 -12
- package/src/contentful/blocks/navigation/Navigation.stories.mocks.tsx +78 -0
- package/src/contentful/blocks/navigation/Navigation.stories.tsx +138 -0
- package/src/contentful/blocks/navigation/desktop-link-groups.tsx/index.test.tsx +208 -0
- package/src/contentful/blocks/navigation/desktop-link-groups.tsx/index.tsx +141 -139
- package/src/contentful/blocks/navigation/index.test.tsx +924 -0
- package/src/contentful/blocks/navigation/index.tsx +569 -568
- package/src/contentful/blocks/navigation/mobile-link-groups.tsx/index.test.tsx +131 -0
- package/src/contentful/blocks/navigation/mobile-link-groups.tsx/index.tsx +82 -82
- package/src/contentful/blocks/navigation/types.ts +71 -71
- package/src/contentful/blocks/primary-hero/PrimaryHero.stories.tsx +23 -23
- package/src/contentful/blocks/primary-hero/index.test.tsx +286 -0
- package/src/contentful/blocks/primary-hero/index.tsx +239 -236
- package/src/contentful/blocks/primary-hero/types.ts +37 -37
- package/src/contentful/blocks/search-block/index.test.tsx +268 -0
- 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.test.tsx +284 -0
- 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.test.tsx +36 -0
- package/src/contentful/blocks/text/index.tsx +12 -12
- package/src/contentful/blocks/text/types.ts +1 -1
- package/src/contentful/index.test.ts +45 -0
- package/src/contentful/index.ts +105 -105
- package/src/global-mocks/contentful/to-document.ts +25 -0
- package/src/global-mocks/cookie.ts +48 -0
- package/src/global-mocks/cx.ts +37 -0
- package/src/global-mocks/index.ts +89 -0
- package/src/global-mocks/speed-card-bg.ts +27 -0
- package/src/global-mocks/utm.ts +49 -0
- package/src/hooks/contentful/use-contentful-rich-text.test.tsx +1758 -0
- package/src/hooks/contentful/use-contentful-rich-text.tsx +309 -309
- package/src/hooks/contentful/use-processed-check-list.test.tsx +277 -0
- package/src/hooks/contentful/use-processed-check-list.ts +63 -63
- package/src/hooks/use-body-scroll-lock.test.ts +134 -0
- package/src/hooks/use-body-scroll-lock.ts +34 -34
- package/src/hooks/use-carousel-swipe.test.ts +393 -0
- package/src/hooks/use-carousel-swipe.ts +264 -264
- package/src/hooks/use-outside-click.test.ts +142 -0
- package/src/hooks/use-outside-click.ts +17 -17
- package/src/index.ts +107 -107
- package/src/next/index.test.ts +7 -0
- package/src/next/index.ts +5 -5
- package/src/setupTests.ts +52 -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.test.ts +85 -0
- package/src/utils/contentful/to-document.ts +24 -24
- package/src/utils/cookie.test.ts +180 -0
- package/src/utils/cookie.ts +84 -84
- package/src/utils/cx.test.ts +90 -0
- package/src/utils/cx.ts +49 -49
- package/src/utils/index.test.ts +115 -0
- package/src/utils/index.ts +41 -41
- package/src/utils/speed-card-bg.test.ts +46 -0
- package/src/utils/speed-card-bg.ts +24 -24
- package/src/utils/utm.test.ts +359 -0
- package/src/utils/utm.ts +221 -221
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { DesktopLinkGroups } from "./index";
|
|
3
|
+
|
|
4
|
+
import { act, fireEvent, render, screen } from "@testing-library/react";
|
|
5
|
+
|
|
6
|
+
jest.mock("@shared/components/material-icon", () => ({
|
|
7
|
+
MaterialIcon: ({ name, className }: any) => (
|
|
8
|
+
<span data-testid={`icon-${name}`} className={className}>
|
|
9
|
+
{name}
|
|
10
|
+
</span>
|
|
11
|
+
),
|
|
12
|
+
}));
|
|
13
|
+
|
|
14
|
+
jest.mock("@shared/components/text", () => ({
|
|
15
|
+
Text: ({ as: Tag = "span", children, className }: any) => (
|
|
16
|
+
<Tag className={className}>{children}</Tag>
|
|
17
|
+
),
|
|
18
|
+
}));
|
|
19
|
+
|
|
20
|
+
jest.mock("@shared/contentful/blocks/button", () => ({
|
|
21
|
+
Button: ({
|
|
22
|
+
children,
|
|
23
|
+
buttonClassName,
|
|
24
|
+
onClick,
|
|
25
|
+
linkClassName,
|
|
26
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
27
|
+
showButtonAs,
|
|
28
|
+
...rest
|
|
29
|
+
}: any) => (
|
|
30
|
+
<button
|
|
31
|
+
data-testid="button"
|
|
32
|
+
className={buttonClassName || linkClassName}
|
|
33
|
+
onClick={onClick}
|
|
34
|
+
aria-expanded={rest["aria-expanded"]}
|
|
35
|
+
>
|
|
36
|
+
{children || rest.buttonLabel}
|
|
37
|
+
</button>
|
|
38
|
+
),
|
|
39
|
+
}));
|
|
40
|
+
|
|
41
|
+
let outsideClickCallback: (() => void) | null = null;
|
|
42
|
+
jest.mock("@shared/hooks/use-outside-click", () => ({
|
|
43
|
+
useOutsideClick: (_ref: any, cb: () => void) => {
|
|
44
|
+
outsideClickCallback = cb;
|
|
45
|
+
},
|
|
46
|
+
}));
|
|
47
|
+
|
|
48
|
+
jest.mock("@shared/utils", () => ({
|
|
49
|
+
cx: (...args: any[]) => args.filter(Boolean).join(" "),
|
|
50
|
+
}));
|
|
51
|
+
|
|
52
|
+
describe("DesktopLinkGroups", () => {
|
|
53
|
+
it("renders nothing when link is undefined", () => {
|
|
54
|
+
const { container } = render(
|
|
55
|
+
<DesktopLinkGroups anchorName="test" link={undefined} />
|
|
56
|
+
);
|
|
57
|
+
expect(container.innerHTML).toBe("");
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("renders single Button when link is a button (has href)", () => {
|
|
61
|
+
const link = {
|
|
62
|
+
href: "/products",
|
|
63
|
+
buttonLabel: "Products",
|
|
64
|
+
anchorId: "prod",
|
|
65
|
+
};
|
|
66
|
+
render(<DesktopLinkGroups anchorName="nav-0" link={link as any} />);
|
|
67
|
+
expect(screen.getByTestId("button")).toHaveTextContent("Products");
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
describe("Group rendering", () => {
|
|
71
|
+
const groupLink = {
|
|
72
|
+
anchorId: "group-1",
|
|
73
|
+
title: "My Account",
|
|
74
|
+
items: {
|
|
75
|
+
items: [
|
|
76
|
+
{ buttonLabel: "Profile", href: "/profile", anchorId: "p1" },
|
|
77
|
+
{ buttonLabel: "Settings", href: "/settings", anchorId: "p2" },
|
|
78
|
+
],
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
it("renders group title button", () => {
|
|
83
|
+
render(
|
|
84
|
+
<DesktopLinkGroups anchorName="account-0" link={groupLink as any} />
|
|
85
|
+
);
|
|
86
|
+
expect(screen.getByText("My Account")).toBeInTheDocument();
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("renders dropdown arrow icon (down when closed)", () => {
|
|
90
|
+
render(
|
|
91
|
+
<DesktopLinkGroups anchorName="account-0" link={groupLink as any} />
|
|
92
|
+
);
|
|
93
|
+
expect(
|
|
94
|
+
screen.getByTestId("icon-keyboard_arrow_down")
|
|
95
|
+
).toBeInTheDocument();
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it("toggles open state and shows up arrow when open", () => {
|
|
99
|
+
render(
|
|
100
|
+
<DesktopLinkGroups anchorName="account-0" link={groupLink as any} />
|
|
101
|
+
);
|
|
102
|
+
const toggleBtn = screen.getAllByTestId("button")[0];
|
|
103
|
+
fireEvent.click(toggleBtn);
|
|
104
|
+
expect(screen.getByTestId("icon-keyboard_arrow_up")).toBeInTheDocument();
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it("renders submenu items when open", () => {
|
|
108
|
+
render(
|
|
109
|
+
<DesktopLinkGroups anchorName="account-0" link={groupLink as any} />
|
|
110
|
+
);
|
|
111
|
+
const toggleBtn = screen.getAllByTestId("button")[0];
|
|
112
|
+
fireEvent.click(toggleBtn);
|
|
113
|
+
expect(screen.getByText("Profile")).toBeInTheDocument();
|
|
114
|
+
expect(screen.getByText("Settings")).toBeInTheDocument();
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it("closes when toggled again", () => {
|
|
118
|
+
render(
|
|
119
|
+
<DesktopLinkGroups anchorName="account-0" link={groupLink as any} />
|
|
120
|
+
);
|
|
121
|
+
const toggleBtn = screen.getAllByTestId("button")[0];
|
|
122
|
+
fireEvent.click(toggleBtn);
|
|
123
|
+
fireEvent.click(toggleBtn);
|
|
124
|
+
expect(
|
|
125
|
+
screen.getByTestId("icon-keyboard_arrow_down")
|
|
126
|
+
).toBeInTheDocument();
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it("renders null title when title is missing", () => {
|
|
130
|
+
const noTitleLink = { ...groupLink, title: null };
|
|
131
|
+
render(
|
|
132
|
+
<DesktopLinkGroups anchorName="account-0" link={noTitleLink as any} />
|
|
133
|
+
);
|
|
134
|
+
// Should still render without error
|
|
135
|
+
expect(screen.getAllByTestId("button")[0]).toBeInTheDocument();
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it("handles empty items array", () => {
|
|
139
|
+
const emptyItems = { ...groupLink, items: { items: [] } };
|
|
140
|
+
render(
|
|
141
|
+
<DesktopLinkGroups anchorName="account-0" link={emptyItems as any} />
|
|
142
|
+
);
|
|
143
|
+
const toggleBtn = screen.getAllByTestId("button")[0];
|
|
144
|
+
fireEvent.click(toggleBtn);
|
|
145
|
+
// No submenu links rendered
|
|
146
|
+
expect(screen.getAllByTestId("button")).toHaveLength(1);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it("handles null items", () => {
|
|
150
|
+
const nullItems = { ...groupLink, items: null };
|
|
151
|
+
render(
|
|
152
|
+
<DesktopLinkGroups anchorName="account-0" link={nullItems as any} />
|
|
153
|
+
);
|
|
154
|
+
const toggleBtn = screen.getAllByTestId("button")[0];
|
|
155
|
+
fireEvent.click(toggleBtn);
|
|
156
|
+
expect(screen.getAllByTestId("button")).toHaveLength(1);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it("closes when a submenu anchor link is clicked", () => {
|
|
160
|
+
render(
|
|
161
|
+
<DesktopLinkGroups anchorName="account-0" link={groupLink as any} />
|
|
162
|
+
);
|
|
163
|
+
const toggleBtn = screen.getAllByTestId("button")[0];
|
|
164
|
+
fireEvent.click(toggleBtn);
|
|
165
|
+
// Simulate clicking an anchor within the submenu ul
|
|
166
|
+
const submenuList = screen.getByRole("list");
|
|
167
|
+
const anchor = document.createElement("a");
|
|
168
|
+
anchor.href = "/profile";
|
|
169
|
+
submenuList.appendChild(anchor);
|
|
170
|
+
fireEvent.click(anchor);
|
|
171
|
+
// After click, arrow should be down (closed)
|
|
172
|
+
expect(
|
|
173
|
+
screen.getByTestId("icon-keyboard_arrow_down")
|
|
174
|
+
).toBeInTheDocument();
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it("does not close when non-anchor element is clicked in submenu", () => {
|
|
178
|
+
render(
|
|
179
|
+
<DesktopLinkGroups anchorName="account-0" link={groupLink as any} />
|
|
180
|
+
);
|
|
181
|
+
const toggleBtn = screen.getAllByTestId("button")[0];
|
|
182
|
+
fireEvent.click(toggleBtn);
|
|
183
|
+
const submenuList = screen.getByRole("list");
|
|
184
|
+
// Click on a non-anchor element
|
|
185
|
+
fireEvent.click(submenuList);
|
|
186
|
+
// Should still be open
|
|
187
|
+
expect(screen.getByTestId("icon-keyboard_arrow_up")).toBeInTheDocument();
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it("closes when outside click is triggered", () => {
|
|
191
|
+
render(
|
|
192
|
+
<DesktopLinkGroups anchorName="account-0" link={groupLink as any} />
|
|
193
|
+
);
|
|
194
|
+
const toggleBtn = screen.getAllByTestId("button")[0];
|
|
195
|
+
fireEvent.click(toggleBtn);
|
|
196
|
+
// Should be open
|
|
197
|
+
expect(screen.getByTestId("icon-keyboard_arrow_up")).toBeInTheDocument();
|
|
198
|
+
// Trigger outside click callback
|
|
199
|
+
act(() => {
|
|
200
|
+
outsideClickCallback!();
|
|
201
|
+
});
|
|
202
|
+
// Should close
|
|
203
|
+
expect(
|
|
204
|
+
screen.getByTestId("icon-keyboard_arrow_down")
|
|
205
|
+
).toBeInTheDocument();
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
});
|
|
@@ -1,139 +1,141 @@
|
|
|
1
|
-
import React, { CSSProperties } from "react";
|
|
2
|
-
|
|
3
|
-
import { MaterialIcon } from "@shared/components/material-icon";
|
|
4
|
-
import { Text } from "@shared/components/text";
|
|
5
|
-
import { Button } from "@shared/contentful/blocks/button";
|
|
6
|
-
import { ButtonProps as ContentfulButtonProps } from "@shared/contentful/blocks/button/types";
|
|
7
|
-
import { useOutsideClick } from "@shared/hooks/use-outside-click";
|
|
8
|
-
import { cx } from "@shared/utils";
|
|
9
|
-
|
|
10
|
-
type ComponentButtonGroup = {
|
|
11
|
-
anchorId?: string | null;
|
|
12
|
-
title?: string | null;
|
|
13
|
-
items?: { items?: ContentfulButtonProps[] | null } | null;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
type Link = ContentfulButtonProps | ComponentButtonGroup;
|
|
17
|
-
|
|
18
|
-
type LinkGroupsProps = {
|
|
19
|
-
link?: Link;
|
|
20
|
-
anchorName: string;
|
|
21
|
-
/**
|
|
22
|
-
* Tailwind text-color class to apply to the top-level link/group
|
|
23
|
-
* label. Lets the parent navbar invert text color (e.g. `text-white`)
|
|
24
|
-
* when rendered on a dark background. Submenu items keep their
|
|
25
|
-
* default `text-text-link` so they remain readable on the white
|
|
26
|
-
* popover background.
|
|
27
|
-
*/
|
|
28
|
-
linkColorClassName?: string;
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
const isButton = (link: Link): link is ContentfulButtonProps => {
|
|
32
|
-
// If your group never has `href`, this is a simple and effective guard
|
|
33
|
-
return typeof (link as ContentfulButtonProps).href === "string";
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
export const DesktopLinkGroups: React.FC<LinkGroupsProps> = ({
|
|
37
|
-
link,
|
|
38
|
-
anchorName,
|
|
39
|
-
linkColorClassName,
|
|
40
|
-
}) => {
|
|
41
|
-
const [isOpen, setIsOpen] = React.useState(false);
|
|
42
|
-
|
|
43
|
-
const ref = React.useRef<HTMLDivElement>(null);
|
|
44
|
-
useOutsideClick(ref, () => setIsOpen(false));
|
|
45
|
-
|
|
46
|
-
if (!link) return null;
|
|
47
|
-
|
|
48
|
-
// Single button
|
|
49
|
-
if (isButton(link)) {
|
|
50
|
-
return (
|
|
51
|
-
<Button
|
|
52
|
-
key={`submenu-link-btn-${link.anchorId}`}
|
|
53
|
-
{...link}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
/>
|
|
60
|
-
);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
const
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
//
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
1
|
+
import React, { CSSProperties } from "react";
|
|
2
|
+
|
|
3
|
+
import { MaterialIcon } from "@shared/components/material-icon";
|
|
4
|
+
import { Text } from "@shared/components/text";
|
|
5
|
+
import { Button } from "@shared/contentful/blocks/button";
|
|
6
|
+
import { ButtonProps as ContentfulButtonProps } from "@shared/contentful/blocks/button/types";
|
|
7
|
+
import { useOutsideClick } from "@shared/hooks/use-outside-click";
|
|
8
|
+
import { cx } from "@shared/utils";
|
|
9
|
+
|
|
10
|
+
type ComponentButtonGroup = {
|
|
11
|
+
anchorId?: string | null;
|
|
12
|
+
title?: string | null;
|
|
13
|
+
items?: { items?: ContentfulButtonProps[] | null } | null;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
type Link = ContentfulButtonProps | ComponentButtonGroup;
|
|
17
|
+
|
|
18
|
+
type LinkGroupsProps = {
|
|
19
|
+
link?: Link;
|
|
20
|
+
anchorName: string;
|
|
21
|
+
/**
|
|
22
|
+
* Tailwind text-color class to apply to the top-level link/group
|
|
23
|
+
* label. Lets the parent navbar invert text color (e.g. `text-white`)
|
|
24
|
+
* when rendered on a dark background. Submenu items keep their
|
|
25
|
+
* default `text-text-link` so they remain readable on the white
|
|
26
|
+
* popover background.
|
|
27
|
+
*/
|
|
28
|
+
linkColorClassName?: string;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const isButton = (link: Link): link is ContentfulButtonProps => {
|
|
32
|
+
// If your group never has `href`, this is a simple and effective guard
|
|
33
|
+
return typeof (link as ContentfulButtonProps).href === "string";
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const DesktopLinkGroups: React.FC<LinkGroupsProps> = ({
|
|
37
|
+
link,
|
|
38
|
+
anchorName,
|
|
39
|
+
linkColorClassName,
|
|
40
|
+
}) => {
|
|
41
|
+
const [isOpen, setIsOpen] = React.useState(false);
|
|
42
|
+
|
|
43
|
+
const ref = React.useRef<HTMLDivElement>(null);
|
|
44
|
+
useOutsideClick(ref, () => setIsOpen(false));
|
|
45
|
+
|
|
46
|
+
if (!link) return null;
|
|
47
|
+
|
|
48
|
+
// Single button
|
|
49
|
+
if (isButton(link)) {
|
|
50
|
+
return (
|
|
51
|
+
<Button
|
|
52
|
+
key={`submenu-link-btn-${link.anchorId}`}
|
|
53
|
+
{...link}
|
|
54
|
+
showButtonAs="text"
|
|
55
|
+
linkClassName={cx(
|
|
56
|
+
"body3 inline-flex items-center no-underline hover:underline",
|
|
57
|
+
linkColorClassName || "text-text"
|
|
58
|
+
)}
|
|
59
|
+
/>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
// Group
|
|
65
|
+
const { anchorId, title, items } = link;
|
|
66
|
+
const subMenu = Array.isArray(items?.items) ? items!.items! : [];
|
|
67
|
+
const fullAnchorName = `--link-anchor-${anchorName}`;
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<div
|
|
71
|
+
className="relative h-full"
|
|
72
|
+
style={{ anchorName: fullAnchorName } as CSSProperties}
|
|
73
|
+
ref={ref}
|
|
74
|
+
>
|
|
75
|
+
<Button
|
|
76
|
+
onClick={() => setIsOpen(prev => !prev)}
|
|
77
|
+
aria-expanded={isOpen}
|
|
78
|
+
buttonClassName={cx(
|
|
79
|
+
"group body3 flex items-center h-full",
|
|
80
|
+
linkColorClassName || "text-text"
|
|
81
|
+
)}
|
|
82
|
+
key={anchorId}
|
|
83
|
+
showButtonAs="unstyled"
|
|
84
|
+
>
|
|
85
|
+
|
|
86
|
+
<Text as="span" className="group-hover:underline leading-none">
|
|
87
|
+
{title ?? null}
|
|
88
|
+
</Text>
|
|
89
|
+
<MaterialIcon
|
|
90
|
+
weight="200"
|
|
91
|
+
size={24}
|
|
92
|
+
className="text-icon-secondary group-hover:opacity-50 mt-[1px]"
|
|
93
|
+
name={isOpen ? "keyboard_arrow_up" : "keyboard_arrow_down"}
|
|
94
|
+
/>
|
|
95
|
+
</Button>
|
|
96
|
+
|
|
97
|
+
<div
|
|
98
|
+
className={cx(
|
|
99
|
+
"shadow-navDrop fixed z-[1001] min-w-44 rounded-input-poppers border border-border bg-bg",
|
|
100
|
+
"transition-[opacity,transform] ease-out",
|
|
101
|
+
isOpen && subMenu.length > 0
|
|
102
|
+
? "pointer-events-auto translate-y-0 opacity-100 duration-75"
|
|
103
|
+
: "pointer-events-none -translate-y-2 opacity-0 duration-0"
|
|
104
|
+
)}
|
|
105
|
+
style={
|
|
106
|
+
{
|
|
107
|
+
positionAnchor: fullAnchorName,
|
|
108
|
+
top: "anchor(bottom)",
|
|
109
|
+
left: "calc((anchor(left) + anchor(right)) / 2)",
|
|
110
|
+
translate: "-50% 0",
|
|
111
|
+
} as CSSProperties
|
|
112
|
+
}
|
|
113
|
+
>
|
|
114
|
+
<ul
|
|
115
|
+
className="flex flex-col gap-2 py-2"
|
|
116
|
+
onClick={event => {
|
|
117
|
+
// Close the popup when a submenu link is clicked.
|
|
118
|
+
// Required for Pages Router where this component persists across
|
|
119
|
+
// client-side navigations and `isOpen` would otherwise stay true.
|
|
120
|
+
const target = event.target as HTMLElement | null;
|
|
121
|
+
if (target?.closest("a")) {
|
|
122
|
+
setIsOpen(false);
|
|
123
|
+
}
|
|
124
|
+
}}
|
|
125
|
+
>
|
|
126
|
+
{subMenu.map((site, index) => {
|
|
127
|
+
return (
|
|
128
|
+
<li key={`submenu-link-${index}`} className="submenu-link">
|
|
129
|
+
<Button
|
|
130
|
+
{...site}
|
|
131
|
+
showButtonAs="text"
|
|
132
|
+
linkClassName="body3 px-3 flex items-center text-text-link no-underline hover:underline hover:bg-bg-surface-hover"
|
|
133
|
+
/>
|
|
134
|
+
</li>
|
|
135
|
+
);
|
|
136
|
+
})}
|
|
137
|
+
</ul>
|
|
138
|
+
</div>
|
|
139
|
+
</div>
|
|
140
|
+
);
|
|
141
|
+
};
|