@windstream/react-shared-components 0.1.93 → 0.1.95
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.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 +2 -2
- 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 +3 -3
- 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 +14 -8
- package/src/components/accordion/index.test.tsx +270 -0
- package/src/components/alert-card/index.test.tsx +152 -0
- package/src/components/animation-wrapper/index.test.tsx +424 -0
- package/src/components/brand-button/index.test.tsx +292 -0
- package/src/components/button/index.test.tsx +91 -0
- package/src/components/call-button/index.test.tsx +260 -0
- package/src/components/checkbox/index.test.tsx +252 -0
- package/src/components/checklist/index.test.tsx +231 -0
- package/src/components/checklist/index.tsx +64 -29
- package/src/components/checklist/types.ts +7 -1
- package/src/components/collapse/index.test.tsx +277 -0
- package/src/components/collapse/index.tsx +1 -0
- package/src/components/divider/index.test.tsx +53 -0
- package/src/components/image/index.test.tsx +174 -0
- package/src/components/input/index.test.tsx +348 -0
- package/src/components/link/index.test.tsx +199 -0
- package/src/components/list/index.test.tsx +166 -0
- package/src/components/material-icon/index.test.tsx +130 -0
- package/src/components/modal/index.test.tsx +310 -0
- package/src/components/next-image/index.test.tsx +406 -0
- package/src/components/pagination/index.test.tsx +521 -0
- package/src/components/radio-button/index.test.tsx +151 -0
- package/src/components/see-more/index.test.tsx +96 -0
- package/src/components/select/index.test.tsx +256 -0
- package/src/components/select-plan-button/index.test.tsx +173 -0
- package/src/components/skeleton/index.test.tsx +74 -0
- package/src/components/spinner/index.test.tsx +76 -0
- package/src/components/text/index.test.tsx +65 -0
- package/src/components/tooltip/index.test.tsx +50 -0
- package/src/components/view-cart-button/index.test.tsx +57 -0
- package/src/contentful/blocks/accordion/index.test.tsx +218 -0
- package/src/contentful/blocks/accordion/index.tsx +3 -1
- package/src/contentful/blocks/address-input-banner/index.test.tsx +132 -0
- package/src/contentful/blocks/anchored-bottom-banner/index.test.tsx +287 -0
- package/src/contentful/blocks/blogs-grid/BlogGrid.stories.tsx +5 -4
- package/src/contentful/blocks/blogs-grid/index.test.tsx +355 -0
- package/src/contentful/blocks/blogs-grid-base/index.test.tsx +274 -0
- package/src/contentful/blocks/breadcrumbs/index.test.tsx +281 -0
- package/src/contentful/blocks/button/index.test.tsx +339 -0
- package/src/contentful/blocks/callout/index.test.tsx +539 -0
- package/src/contentful/blocks/cards/blog-card/index.test.tsx +218 -0
- package/src/contentful/blocks/cards/floating-image-card/index.test.tsx +201 -0
- package/src/contentful/blocks/cards/full-image-card/index.test.tsx +216 -0
- package/src/contentful/blocks/cards/index.test.tsx +39 -0
- package/src/contentful/blocks/cards/product-card/index.test.tsx +263 -0
- package/src/contentful/blocks/cards/simple-card/index.test.tsx +364 -0
- package/src/contentful/blocks/cards/simple-card/index.tsx +1 -1
- package/src/contentful/blocks/cards/testimonial-card/index.test.tsx +180 -0
- package/src/contentful/blocks/carousel/helper.test.tsx +539 -0
- package/src/contentful/blocks/carousel/index.test.tsx +308 -0
- package/src/contentful/blocks/carousel/types.test.ts +16 -0
- package/src/contentful/blocks/cart-retention-banner/index.test.tsx +409 -0
- package/src/contentful/blocks/cart-retention-banner/index.tsx +4 -4
- package/src/contentful/blocks/comparison-table/index.test.tsx +114 -0
- package/src/contentful/blocks/cookiebanner/index.test.tsx +277 -0
- package/src/contentful/blocks/cta-callout/index.test.tsx +244 -0
- package/src/contentful/blocks/dynamic-tabs/index.test.tsx +240 -0
- package/src/contentful/blocks/email-input-block/index.test.tsx +213 -0
- package/src/contentful/blocks/email-input-block/index.tsx +40 -35
- package/src/contentful/blocks/find-kinetic/index.test.tsx +269 -0
- package/src/contentful/blocks/floating-banner/index.test.tsx +246 -0
- package/src/contentful/blocks/footer/index.test.tsx +302 -0
- package/src/contentful/blocks/image-promo-bar/helper.test.tsx +61 -0
- package/src/contentful/blocks/image-promo-bar/index.test.tsx +467 -0
- package/src/contentful/blocks/image-promo-bar/index.tsx +248 -246
- package/src/contentful/blocks/image-promo-bar/vimeo-embed.test.tsx +142 -0
- package/src/contentful/blocks/image-promo-bar/youtube-embed.test.tsx +104 -0
- package/src/contentful/blocks/modal/index.test.tsx +209 -0
- package/src/contentful/blocks/navigation/desktop-link-groups.tsx/index.test.tsx +208 -0
- package/src/contentful/blocks/navigation/index.test.tsx +924 -0
- package/src/contentful/blocks/navigation/mobile-link-groups.tsx/index.test.tsx +131 -0
- package/src/contentful/blocks/primary-hero/index.test.tsx +286 -0
- package/src/contentful/blocks/primary-hero/index.tsx +7 -4
- package/src/contentful/blocks/search-block/index.test.tsx +268 -0
- package/src/contentful/blocks/shape-background-wrapper/index.test.tsx +284 -0
- package/src/contentful/blocks/text/index.test.tsx +36 -0
- package/src/contentful/index.test.ts +45 -0
- 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 +1 -1
- package/src/hooks/contentful/use-processed-check-list.test.tsx +277 -0
- package/src/hooks/use-body-scroll-lock.test.ts +134 -0
- package/src/hooks/use-carousel-swipe.test.ts +393 -0
- package/src/hooks/use-outside-click.test.ts +142 -0
- package/src/index.ts +1 -1
- package/src/next/index.test.ts +7 -0
- package/src/setupTests.ts +17 -11
- package/src/utils/contentful/to-document.test.ts +85 -0
- package/src/utils/cookie.test.ts +180 -0
- package/src/utils/cx.test.ts +90 -0
- package/src/utils/index.test.ts +115 -0
- package/src/utils/speed-card-bg.test.ts +46 -0
- package/src/utils/utm.test.ts +359 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Text } from "./index";
|
|
3
|
+
|
|
4
|
+
import { render, screen } from "@testing-library/react";
|
|
5
|
+
|
|
6
|
+
describe("Text", () => {
|
|
7
|
+
it("renders children as a p element by default", () => {
|
|
8
|
+
render(<Text>Hello</Text>);
|
|
9
|
+
const el = screen.getByText("Hello");
|
|
10
|
+
expect(el.tagName).toBe("P");
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it("renders as a custom element when 'as' prop is provided", () => {
|
|
14
|
+
render(<Text as="h1">Heading</Text>);
|
|
15
|
+
const el = screen.getByText("Heading");
|
|
16
|
+
expect(el.tagName).toBe("H1");
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("renders as a span", () => {
|
|
20
|
+
render(<Text as="span">Inline</Text>);
|
|
21
|
+
expect(screen.getByText("Inline").tagName).toBe("SPAN");
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("applies className", () => {
|
|
25
|
+
render(<Text className="text-bold">Styled</Text>);
|
|
26
|
+
expect(screen.getByText("Styled")).toHaveClass("text-bold");
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("uses empty string as default className", () => {
|
|
30
|
+
render(<Text>No class</Text>);
|
|
31
|
+
expect(screen.getByText("No class")).toHaveAttribute("class", "");
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("applies inline style", () => {
|
|
35
|
+
render(<Text style={{ color: "red" }}>Red</Text>);
|
|
36
|
+
expect(screen.getByText("Red").style.color).toBe("red");
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("passes additional HTML attributes", () => {
|
|
40
|
+
render(
|
|
41
|
+
<Text data-testid="custom-text" id="my-text">
|
|
42
|
+
Attr
|
|
43
|
+
</Text>
|
|
44
|
+
);
|
|
45
|
+
const el = screen.getByTestId("custom-text");
|
|
46
|
+
expect(el).toHaveAttribute("id", "my-text");
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("forwards ref to the element", () => {
|
|
50
|
+
const ref = React.createRef<HTMLElement>();
|
|
51
|
+
render(<Text ref={ref}>Ref test</Text>);
|
|
52
|
+
expect(ref.current).toBeInstanceOf(HTMLParagraphElement);
|
|
53
|
+
expect(ref.current?.textContent).toBe("Ref test");
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("renders without children", () => {
|
|
57
|
+
const { container } = render(<Text />);
|
|
58
|
+
expect(container.querySelector("p")).toBeInTheDocument();
|
|
59
|
+
expect(container.querySelector("p")?.textContent).toBe("");
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("has the correct displayName", () => {
|
|
63
|
+
expect(Text.displayName).toBe("Text");
|
|
64
|
+
});
|
|
65
|
+
});
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { Tooltip } from "./index";
|
|
2
|
+
|
|
3
|
+
import { render, screen } from "@testing-library/react";
|
|
4
|
+
|
|
5
|
+
describe("Tooltip", () => {
|
|
6
|
+
it("renders children", () => {
|
|
7
|
+
render(<Tooltip tooltipMsg="Info">Hover me</Tooltip>);
|
|
8
|
+
expect(screen.getByText("Hover me")).toBeInTheDocument();
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it("sets data-text attribute with tooltip message", () => {
|
|
12
|
+
render(<Tooltip tooltipMsg="Helpful tip">Content</Tooltip>);
|
|
13
|
+
const span = screen.getByText("Content");
|
|
14
|
+
expect(span).toHaveAttribute("data-text", "Helpful tip");
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it("applies tooltip-custom class to the span", () => {
|
|
18
|
+
render(<Tooltip tooltipMsg="Tip">Child</Tooltip>);
|
|
19
|
+
const span = screen.getByText("Child");
|
|
20
|
+
expect(span).toHaveClass("tooltip-custom");
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("applies custom className when provided", () => {
|
|
24
|
+
render(
|
|
25
|
+
<Tooltip tooltipMsg="Tip" className="extra-class">
|
|
26
|
+
Child
|
|
27
|
+
</Tooltip>
|
|
28
|
+
);
|
|
29
|
+
const span = screen.getByText("Child");
|
|
30
|
+
expect(span).toHaveClass("tooltip-custom");
|
|
31
|
+
expect(span).toHaveClass("extra-class");
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("does not add undefined to class when className is not provided", () => {
|
|
35
|
+
render(<Tooltip tooltipMsg="Tip">Child</Tooltip>);
|
|
36
|
+
const span = screen.getByText("Child");
|
|
37
|
+
expect(span.className).not.toContain("undefined");
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it("injects a style element", () => {
|
|
41
|
+
const { container } = render(<Tooltip tooltipMsg="Tip">Child</Tooltip>);
|
|
42
|
+
const style = container.querySelector("style");
|
|
43
|
+
expect(style).toBeInTheDocument();
|
|
44
|
+
expect(style?.textContent).toContain(".tooltip-custom");
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("has the correct displayName", () => {
|
|
48
|
+
expect(Tooltip.displayName).toBe("Tooltip");
|
|
49
|
+
});
|
|
50
|
+
});
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { ViewCartButton } from "./index";
|
|
2
|
+
|
|
3
|
+
import { fireEvent, render, screen } from "@testing-library/react";
|
|
4
|
+
|
|
5
|
+
describe("ViewCartButton", () => {
|
|
6
|
+
const defaultProps = {
|
|
7
|
+
cartTotalText: "$49.99/mo",
|
|
8
|
+
onClick: jest.fn(),
|
|
9
|
+
isOpen: false,
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
jest.clearAllMocks();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("renders 'View cart' label", () => {
|
|
17
|
+
render(<ViewCartButton {...defaultProps} />);
|
|
18
|
+
expect(screen.getByText("View cart")).toBeInTheDocument();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("renders cart total text", () => {
|
|
22
|
+
render(<ViewCartButton {...defaultProps} />);
|
|
23
|
+
expect(screen.getByTestId("cart-total")).toHaveTextContent("$49.99/mo");
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("calls onClick when clicked", () => {
|
|
27
|
+
render(<ViewCartButton {...defaultProps} />);
|
|
28
|
+
fireEvent.click(screen.getByRole("button"));
|
|
29
|
+
expect(defaultProps.onClick).toHaveBeenCalledTimes(1);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("renders arrow icon", () => {
|
|
33
|
+
render(<ViewCartButton {...defaultProps} />);
|
|
34
|
+
expect(screen.getByText("keyboard_arrow_up")).toBeInTheDocument();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("applies rotate-0 class when isOpen is false", () => {
|
|
38
|
+
render(<ViewCartButton {...defaultProps} isOpen={false} />);
|
|
39
|
+
const icon = screen.getByText("keyboard_arrow_up");
|
|
40
|
+
expect(icon.className).toContain("rotate-0");
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("applies rotate-180 class when isOpen is true", () => {
|
|
44
|
+
render(<ViewCartButton {...defaultProps} isOpen={true} />);
|
|
45
|
+
const icon = screen.getByText("keyboard_arrow_up");
|
|
46
|
+
expect(icon.className).toContain("rotate-180");
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("passes additional button attributes", () => {
|
|
50
|
+
render(
|
|
51
|
+
<ViewCartButton {...defaultProps} disabled={true} aria-label="cart" />
|
|
52
|
+
);
|
|
53
|
+
const button = screen.getByRole("button");
|
|
54
|
+
expect(button).toBeDisabled();
|
|
55
|
+
expect(button).toHaveAttribute("aria-label", "cart");
|
|
56
|
+
});
|
|
57
|
+
});
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import Accordion from "./index";
|
|
2
|
+
import { AccordionItem } from "./types";
|
|
3
|
+
|
|
4
|
+
import { fireEvent, render, screen } from "@testing-library/react";
|
|
5
|
+
|
|
6
|
+
const items: AccordionItem[] = [
|
|
7
|
+
{ anchorId: "item-1", title: "First Question", description: "Answer one" },
|
|
8
|
+
{ anchorId: "item-2", title: "Second Question", description: "Answer two" },
|
|
9
|
+
{ anchorId: "item-3", title: "Third Question", description: "Answer three" },
|
|
10
|
+
];
|
|
11
|
+
|
|
12
|
+
describe("Contentful Accordion", () => {
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
Object.defineProperty(window, "location", {
|
|
15
|
+
writable: true,
|
|
16
|
+
value: { hash: "" },
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("renders the title as h2 by default", () => {
|
|
21
|
+
render(<Accordion items={items} title="FAQ" />);
|
|
22
|
+
const heading = screen.getByText("FAQ");
|
|
23
|
+
expect(heading.tagName).toBe("H2");
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("renders the title as h1 when enableHeading is true", () => {
|
|
27
|
+
render(<Accordion items={items} title="FAQ" enableHeading={true} />);
|
|
28
|
+
const heading = screen.getByText("FAQ");
|
|
29
|
+
expect(heading.tagName).toBe("H1");
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("renders all accordion item titles", () => {
|
|
33
|
+
render(<Accordion items={items} title="FAQ" />);
|
|
34
|
+
expect(screen.getByText("First Question")).toBeInTheDocument();
|
|
35
|
+
expect(screen.getByText("Second Question")).toBeInTheDocument();
|
|
36
|
+
expect(screen.getByText("Third Question")).toBeInTheDocument();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("renders item descriptions", () => {
|
|
40
|
+
render(<Accordion items={items} title="FAQ" />);
|
|
41
|
+
expect(screen.getByText("Answer one")).toBeInTheDocument();
|
|
42
|
+
expect(screen.getByText("Answer two")).toBeInTheDocument();
|
|
43
|
+
expect(screen.getByText("Answer three")).toBeInTheDocument();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("applies anchorId to the section", () => {
|
|
47
|
+
const { container } = render(
|
|
48
|
+
<Accordion items={items} title="FAQ" anchorId="faq-section" />
|
|
49
|
+
);
|
|
50
|
+
expect(container.querySelector("#faq-section")).toBeInTheDocument();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("applies white background by default", () => {
|
|
54
|
+
const { container } = render(<Accordion items={items} title="FAQ" />);
|
|
55
|
+
expect(container.querySelector(".bg-white")).toBeInTheDocument();
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("applies navy background", () => {
|
|
59
|
+
const { container } = render(
|
|
60
|
+
<Accordion items={items} title="FAQ" background="navy" />
|
|
61
|
+
);
|
|
62
|
+
expect(
|
|
63
|
+
container.querySelector('section[class*="bg-[#00002D]"]')
|
|
64
|
+
).toBeInTheDocument();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("applies blue background", () => {
|
|
68
|
+
const { container } = render(
|
|
69
|
+
<Accordion items={items} title="FAQ" background="blue" />
|
|
70
|
+
);
|
|
71
|
+
expect(
|
|
72
|
+
container.querySelector('section[class*="bg-[#07B2E2]"]')
|
|
73
|
+
).toBeInTheDocument();
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("applies green background", () => {
|
|
77
|
+
const { container } = render(
|
|
78
|
+
<Accordion items={items} title="FAQ" background="green" />
|
|
79
|
+
);
|
|
80
|
+
expect(
|
|
81
|
+
container.querySelector('section[class*="bg-[#26B170]"]')
|
|
82
|
+
).toBeInTheDocument();
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("applies yellow background", () => {
|
|
86
|
+
const { container } = render(
|
|
87
|
+
<Accordion items={items} title="FAQ" background="yellow" />
|
|
88
|
+
);
|
|
89
|
+
expect(
|
|
90
|
+
container.querySelector('section[class*="bg-[#F5FF1E]"]')
|
|
91
|
+
).toBeInTheDocument();
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it("applies purple background", () => {
|
|
95
|
+
const { container } = render(
|
|
96
|
+
<Accordion items={items} title="FAQ" background="purple" />
|
|
97
|
+
);
|
|
98
|
+
expect(
|
|
99
|
+
container.querySelector('section[class*="bg-[#931D69]"]')
|
|
100
|
+
).toBeInTheDocument();
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it("applies cream500 background", () => {
|
|
104
|
+
const { container } = render(
|
|
105
|
+
<Accordion items={items} title="FAQ" background="cream500" />
|
|
106
|
+
);
|
|
107
|
+
expect(
|
|
108
|
+
container.querySelector('section[class*="bg-[#FFFEEF]"]')
|
|
109
|
+
).toBeInTheDocument();
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it("applies maxWidth class by default", () => {
|
|
113
|
+
const { container } = render(<Accordion items={items} title="FAQ" />);
|
|
114
|
+
expect(container.querySelector(".max-w-120")).toBeInTheDocument();
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it("does not apply maxWidth class when maxWidth is false", () => {
|
|
118
|
+
const { container } = render(
|
|
119
|
+
<Accordion items={items} title="FAQ" maxWidth={false} />
|
|
120
|
+
);
|
|
121
|
+
expect(container.querySelector(".max-w-120")).not.toBeInTheDocument();
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it("renders dividers between items but not after the last", () => {
|
|
125
|
+
const { container } = render(<Accordion items={items} title="FAQ" />);
|
|
126
|
+
const dividers = container.querySelectorAll(
|
|
127
|
+
".bg-bg-surface-tertiary-active"
|
|
128
|
+
);
|
|
129
|
+
expect(dividers).toHaveLength(items.length - 1);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it("expands items in initialExpandedItems", () => {
|
|
133
|
+
render(
|
|
134
|
+
<Accordion items={items} title="FAQ" initialExpandedItems={["item-1"]} />
|
|
135
|
+
);
|
|
136
|
+
const arrowsUp = screen.getAllByText("keyboard_arrow_up");
|
|
137
|
+
expect(arrowsUp.length).toBeGreaterThanOrEqual(1);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it("auto-expands item matching URL hash when hashAutoExpand is true", () => {
|
|
141
|
+
Object.defineProperty(window, "location", {
|
|
142
|
+
writable: true,
|
|
143
|
+
value: { hash: "#item-2" },
|
|
144
|
+
});
|
|
145
|
+
render(<Accordion items={items} title="FAQ" hashAutoExpand={true} />);
|
|
146
|
+
expect(document.getElementById("item-2")).toBeInTheDocument();
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it("does not auto-expand when hashAutoExpand is false", () => {
|
|
150
|
+
Object.defineProperty(window, "location", {
|
|
151
|
+
writable: true,
|
|
152
|
+
value: { hash: "#item-2" },
|
|
153
|
+
});
|
|
154
|
+
render(<Accordion items={items} title="FAQ" hashAutoExpand={false} />);
|
|
155
|
+
const arrowsDown = screen.getAllByText("keyboard_arrow_down");
|
|
156
|
+
expect(arrowsDown.length).toBe(items.length);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it("does not duplicate item in expandedItems if already present", () => {
|
|
160
|
+
Object.defineProperty(window, "location", {
|
|
161
|
+
writable: true,
|
|
162
|
+
value: { hash: "#item-1" },
|
|
163
|
+
});
|
|
164
|
+
render(
|
|
165
|
+
<Accordion
|
|
166
|
+
items={items}
|
|
167
|
+
title="FAQ"
|
|
168
|
+
hashAutoExpand={true}
|
|
169
|
+
initialExpandedItems={["item-1"]}
|
|
170
|
+
/>
|
|
171
|
+
);
|
|
172
|
+
expect(document.getElementById("item-1")).toBeInTheDocument();
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it("applies itemContainerClassName", () => {
|
|
176
|
+
const { container } = render(
|
|
177
|
+
<Accordion items={items} title="FAQ" itemContainerClassName="gap-4" />
|
|
178
|
+
);
|
|
179
|
+
expect(container.querySelector(".gap-4")).toBeInTheDocument();
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it("applies descriptionMaxWidth class", () => {
|
|
183
|
+
const { container } = render(
|
|
184
|
+
<Accordion
|
|
185
|
+
items={items}
|
|
186
|
+
title="FAQ"
|
|
187
|
+
descriptionMaxWidth="max-w-[500px]"
|
|
188
|
+
/>
|
|
189
|
+
);
|
|
190
|
+
expect(container.querySelector(".max-w-\\[500px\\]")).toBeInTheDocument();
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it("sets id on items with anchorId", () => {
|
|
194
|
+
render(<Accordion items={items} title="FAQ" />);
|
|
195
|
+
expect(document.getElementById("item-1")).toBeInTheDocument();
|
|
196
|
+
expect(document.getElementById("item-2")).toBeInTheDocument();
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it("handles items without anchorId using index as key", () => {
|
|
200
|
+
const noAnchorItems: AccordionItem[] = [
|
|
201
|
+
{ title: "Q1", description: "A1" },
|
|
202
|
+
{ title: "Q2", description: "A2" },
|
|
203
|
+
];
|
|
204
|
+
render(<Accordion items={noAnchorItems} title="FAQ" />);
|
|
205
|
+
expect(screen.getByText("Q1")).toBeInTheDocument();
|
|
206
|
+
expect(screen.getByText("Q2")).toBeInTheDocument();
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it("toggles expanded state when an accordion item button is clicked", () => {
|
|
210
|
+
render(
|
|
211
|
+
<Accordion items={items} title="FAQ" initialExpandedItems={["item-1"]} />
|
|
212
|
+
);
|
|
213
|
+
const buttons = screen.getAllByRole("button");
|
|
214
|
+
fireEvent.click(buttons[0]);
|
|
215
|
+
fireEvent.click(buttons[0]);
|
|
216
|
+
expect(screen.getByText("First Question")).toBeInTheDocument();
|
|
217
|
+
});
|
|
218
|
+
});
|
|
@@ -36,6 +36,7 @@ export const Accordion: React.FC<AccordionProps> = ({
|
|
|
36
36
|
}
|
|
37
37
|
}, [hashAutoExpand]);
|
|
38
38
|
|
|
39
|
+
/* istanbul ignore next -- passed to AccordionComponent which does not forward onClick */
|
|
39
40
|
const handleToggle = (itemAnchorId: string) => {
|
|
40
41
|
setExpandedItems(prev =>
|
|
41
42
|
prev.includes(itemAnchorId)
|
|
@@ -90,7 +91,8 @@ export const Accordion: React.FC<AccordionProps> = ({
|
|
|
90
91
|
borderRadiusNone={true}
|
|
91
92
|
openOnlyOnDesktop={false}
|
|
92
93
|
{...(item.anchorId && {
|
|
93
|
-
onClick: () =>
|
|
94
|
+
onClick: /* istanbul ignore next */ () =>
|
|
95
|
+
handleToggle(item.anchorId!),
|
|
94
96
|
})}
|
|
95
97
|
>
|
|
96
98
|
<div className={descriptionMaxWidth}>
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { AddressInputBanner } from "./index";
|
|
2
|
+
|
|
3
|
+
import { render, screen } from "@testing-library/react";
|
|
4
|
+
|
|
5
|
+
describe("AddressInputBanner", () => {
|
|
6
|
+
const defaultProps = {
|
|
7
|
+
title: "Check availability",
|
|
8
|
+
isVisible: true,
|
|
9
|
+
navHeight: 64,
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
it("renders title when visible", () => {
|
|
13
|
+
render(<AddressInputBanner {...defaultProps} />);
|
|
14
|
+
expect(screen.getByText("Check availability")).toBeInTheDocument();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it("returns null when isVisible is false", () => {
|
|
18
|
+
const { container } = render(
|
|
19
|
+
<AddressInputBanner {...defaultProps} isVisible={false} />
|
|
20
|
+
);
|
|
21
|
+
expect(container.firstChild).toBeNull();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("applies sticky top style from navHeight", () => {
|
|
25
|
+
const { container } = render(
|
|
26
|
+
<AddressInputBanner {...defaultProps} navHeight={80} />
|
|
27
|
+
);
|
|
28
|
+
const div = container.firstChild as HTMLElement;
|
|
29
|
+
expect(div.style.top).toBe("80px");
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("applies yellow variant styles by default", () => {
|
|
33
|
+
const { container } = render(<AddressInputBanner {...defaultProps} />);
|
|
34
|
+
const div = container.firstChild as HTMLElement;
|
|
35
|
+
expect(div.className).toContain("bg-bg-fill-brand-accent");
|
|
36
|
+
expect(div.className).toContain("text-text");
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("applies white variant styles", () => {
|
|
40
|
+
const { container } = render(
|
|
41
|
+
<AddressInputBanner {...defaultProps} variant="white" />
|
|
42
|
+
);
|
|
43
|
+
const div = container.firstChild as HTMLElement;
|
|
44
|
+
expect(div.className).toContain("bg-white");
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("applies navy variant styles", () => {
|
|
48
|
+
const { container } = render(
|
|
49
|
+
<AddressInputBanner {...defaultProps} variant="navy" />
|
|
50
|
+
);
|
|
51
|
+
const div = container.firstChild as HTMLElement;
|
|
52
|
+
expect(div.className).toContain("bg-bg-fill-inverse");
|
|
53
|
+
expect(div.className).toContain("text-text-inverse");
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("applies green variant styles", () => {
|
|
57
|
+
const { container } = render(
|
|
58
|
+
<AddressInputBanner {...defaultProps} variant="green" />
|
|
59
|
+
);
|
|
60
|
+
const div = container.firstChild as HTMLElement;
|
|
61
|
+
expect(div.className).toContain("bg-bg-fill-success");
|
|
62
|
+
expect(div.className).toContain("text-text-inverse");
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("falls back to yellow for unknown variant", () => {
|
|
66
|
+
const { container } = render(
|
|
67
|
+
<AddressInputBanner {...defaultProps} variant="unknown" />
|
|
68
|
+
);
|
|
69
|
+
const div = container.firstChild as HTMLElement;
|
|
70
|
+
expect(div.className).toContain("bg-bg-fill-brand-accent");
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("is case-insensitive for variant", () => {
|
|
74
|
+
const { container } = render(
|
|
75
|
+
<AddressInputBanner {...defaultProps} variant="Navy" />
|
|
76
|
+
);
|
|
77
|
+
const div = container.firstChild as HTMLElement;
|
|
78
|
+
expect(div.className).toContain("bg-bg-fill-inverse");
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("renders renderCheckPlans output", () => {
|
|
82
|
+
const renderCheckPlans = jest.fn(() => (
|
|
83
|
+
<div data-testid="check-plans">Plans</div>
|
|
84
|
+
));
|
|
85
|
+
render(
|
|
86
|
+
<AddressInputBanner
|
|
87
|
+
{...defaultProps}
|
|
88
|
+
renderCheckPlans={renderCheckPlans}
|
|
89
|
+
/>
|
|
90
|
+
);
|
|
91
|
+
expect(screen.getByTestId("check-plans")).toBeInTheDocument();
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it("calls renderCheckPlans with correct arguments", () => {
|
|
95
|
+
const renderCheckPlans = jest.fn(() => null);
|
|
96
|
+
const cta = {
|
|
97
|
+
buttonLabel: "See plans",
|
|
98
|
+
buttonVariant: "primary_brand",
|
|
99
|
+
} as any;
|
|
100
|
+
render(
|
|
101
|
+
<AddressInputBanner
|
|
102
|
+
{...defaultProps}
|
|
103
|
+
cta={cta}
|
|
104
|
+
renderCheckPlans={renderCheckPlans}
|
|
105
|
+
/>
|
|
106
|
+
);
|
|
107
|
+
expect(renderCheckPlans).toHaveBeenCalledWith({
|
|
108
|
+
ctaText: "See plans",
|
|
109
|
+
buttonVariant: "primary_brand",
|
|
110
|
+
showButtonAs: "solid",
|
|
111
|
+
cta,
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("uses default ctaLabel 'Check plans' when cta.buttonLabel is not provided", () => {
|
|
116
|
+
const renderCheckPlans = jest.fn(() => null);
|
|
117
|
+
render(
|
|
118
|
+
<AddressInputBanner
|
|
119
|
+
{...defaultProps}
|
|
120
|
+
renderCheckPlans={renderCheckPlans}
|
|
121
|
+
/>
|
|
122
|
+
);
|
|
123
|
+
expect(renderCheckPlans).toHaveBeenCalledWith(
|
|
124
|
+
expect.objectContaining({ ctaText: "Check plans" })
|
|
125
|
+
);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it("does not crash without renderCheckPlans", () => {
|
|
129
|
+
const { container } = render(<AddressInputBanner {...defaultProps} />);
|
|
130
|
+
expect(container.firstChild).toBeInTheDocument();
|
|
131
|
+
});
|
|
132
|
+
});
|