@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.
Files changed (112) hide show
  1. package/dist/contentful/index.esm.js +2 -2
  2. package/dist/contentful/index.esm.js.map +1 -1
  3. package/dist/contentful/index.js +3 -3
  4. package/dist/contentful/index.js.map +1 -1
  5. package/dist/core.d.ts +2 -2
  6. package/dist/index.d.ts +2 -2
  7. package/dist/index.esm.js +1 -1
  8. package/dist/index.esm.js.map +1 -1
  9. package/dist/index.js +3 -3
  10. package/dist/index.js.map +1 -1
  11. package/dist/styles.css +1 -1
  12. package/dist/utils/index.esm.js +1 -1
  13. package/dist/utils/index.js +1 -1
  14. package/package.json +14 -8
  15. package/src/components/accordion/index.test.tsx +270 -0
  16. package/src/components/alert-card/index.test.tsx +152 -0
  17. package/src/components/animation-wrapper/index.test.tsx +424 -0
  18. package/src/components/brand-button/index.test.tsx +292 -0
  19. package/src/components/button/index.test.tsx +91 -0
  20. package/src/components/call-button/index.test.tsx +260 -0
  21. package/src/components/checkbox/index.test.tsx +252 -0
  22. package/src/components/checklist/index.test.tsx +231 -0
  23. package/src/components/checklist/index.tsx +64 -29
  24. package/src/components/checklist/types.ts +7 -1
  25. package/src/components/collapse/index.test.tsx +277 -0
  26. package/src/components/collapse/index.tsx +1 -0
  27. package/src/components/divider/index.test.tsx +53 -0
  28. package/src/components/image/index.test.tsx +174 -0
  29. package/src/components/input/index.test.tsx +348 -0
  30. package/src/components/link/index.test.tsx +199 -0
  31. package/src/components/list/index.test.tsx +166 -0
  32. package/src/components/material-icon/index.test.tsx +130 -0
  33. package/src/components/modal/index.test.tsx +310 -0
  34. package/src/components/next-image/index.test.tsx +406 -0
  35. package/src/components/pagination/index.test.tsx +521 -0
  36. package/src/components/radio-button/index.test.tsx +151 -0
  37. package/src/components/see-more/index.test.tsx +96 -0
  38. package/src/components/select/index.test.tsx +256 -0
  39. package/src/components/select-plan-button/index.test.tsx +173 -0
  40. package/src/components/skeleton/index.test.tsx +74 -0
  41. package/src/components/spinner/index.test.tsx +76 -0
  42. package/src/components/text/index.test.tsx +65 -0
  43. package/src/components/tooltip/index.test.tsx +50 -0
  44. package/src/components/view-cart-button/index.test.tsx +57 -0
  45. package/src/contentful/blocks/accordion/index.test.tsx +218 -0
  46. package/src/contentful/blocks/accordion/index.tsx +3 -1
  47. package/src/contentful/blocks/address-input-banner/index.test.tsx +132 -0
  48. package/src/contentful/blocks/anchored-bottom-banner/index.test.tsx +287 -0
  49. package/src/contentful/blocks/blogs-grid/BlogGrid.stories.tsx +5 -4
  50. package/src/contentful/blocks/blogs-grid/index.test.tsx +355 -0
  51. package/src/contentful/blocks/blogs-grid-base/index.test.tsx +274 -0
  52. package/src/contentful/blocks/breadcrumbs/index.test.tsx +281 -0
  53. package/src/contentful/blocks/button/index.test.tsx +339 -0
  54. package/src/contentful/blocks/callout/index.test.tsx +539 -0
  55. package/src/contentful/blocks/cards/blog-card/index.test.tsx +218 -0
  56. package/src/contentful/blocks/cards/floating-image-card/index.test.tsx +201 -0
  57. package/src/contentful/blocks/cards/full-image-card/index.test.tsx +216 -0
  58. package/src/contentful/blocks/cards/index.test.tsx +39 -0
  59. package/src/contentful/blocks/cards/product-card/index.test.tsx +263 -0
  60. package/src/contentful/blocks/cards/simple-card/index.test.tsx +364 -0
  61. package/src/contentful/blocks/cards/simple-card/index.tsx +1 -1
  62. package/src/contentful/blocks/cards/testimonial-card/index.test.tsx +180 -0
  63. package/src/contentful/blocks/carousel/helper.test.tsx +539 -0
  64. package/src/contentful/blocks/carousel/index.test.tsx +308 -0
  65. package/src/contentful/blocks/carousel/types.test.ts +16 -0
  66. package/src/contentful/blocks/cart-retention-banner/index.test.tsx +409 -0
  67. package/src/contentful/blocks/cart-retention-banner/index.tsx +4 -4
  68. package/src/contentful/blocks/comparison-table/index.test.tsx +114 -0
  69. package/src/contentful/blocks/cookiebanner/index.test.tsx +277 -0
  70. package/src/contentful/blocks/cta-callout/index.test.tsx +244 -0
  71. package/src/contentful/blocks/dynamic-tabs/index.test.tsx +240 -0
  72. package/src/contentful/blocks/email-input-block/index.test.tsx +213 -0
  73. package/src/contentful/blocks/email-input-block/index.tsx +40 -35
  74. package/src/contentful/blocks/find-kinetic/index.test.tsx +269 -0
  75. package/src/contentful/blocks/floating-banner/index.test.tsx +246 -0
  76. package/src/contentful/blocks/footer/index.test.tsx +302 -0
  77. package/src/contentful/blocks/image-promo-bar/helper.test.tsx +61 -0
  78. package/src/contentful/blocks/image-promo-bar/index.test.tsx +467 -0
  79. package/src/contentful/blocks/image-promo-bar/index.tsx +248 -246
  80. package/src/contentful/blocks/image-promo-bar/vimeo-embed.test.tsx +142 -0
  81. package/src/contentful/blocks/image-promo-bar/youtube-embed.test.tsx +104 -0
  82. package/src/contentful/blocks/modal/index.test.tsx +209 -0
  83. package/src/contentful/blocks/navigation/desktop-link-groups.tsx/index.test.tsx +208 -0
  84. package/src/contentful/blocks/navigation/index.test.tsx +924 -0
  85. package/src/contentful/blocks/navigation/mobile-link-groups.tsx/index.test.tsx +131 -0
  86. package/src/contentful/blocks/primary-hero/index.test.tsx +286 -0
  87. package/src/contentful/blocks/primary-hero/index.tsx +7 -4
  88. package/src/contentful/blocks/search-block/index.test.tsx +268 -0
  89. package/src/contentful/blocks/shape-background-wrapper/index.test.tsx +284 -0
  90. package/src/contentful/blocks/text/index.test.tsx +36 -0
  91. package/src/contentful/index.test.ts +45 -0
  92. package/src/global-mocks/contentful/to-document.ts +25 -0
  93. package/src/global-mocks/cookie.ts +48 -0
  94. package/src/global-mocks/cx.ts +37 -0
  95. package/src/global-mocks/index.ts +89 -0
  96. package/src/global-mocks/speed-card-bg.ts +27 -0
  97. package/src/global-mocks/utm.ts +49 -0
  98. package/src/hooks/contentful/use-contentful-rich-text.test.tsx +1758 -0
  99. package/src/hooks/contentful/use-contentful-rich-text.tsx +1 -1
  100. package/src/hooks/contentful/use-processed-check-list.test.tsx +277 -0
  101. package/src/hooks/use-body-scroll-lock.test.ts +134 -0
  102. package/src/hooks/use-carousel-swipe.test.ts +393 -0
  103. package/src/hooks/use-outside-click.test.ts +142 -0
  104. package/src/index.ts +1 -1
  105. package/src/next/index.test.ts +7 -0
  106. package/src/setupTests.ts +17 -11
  107. package/src/utils/contentful/to-document.test.ts +85 -0
  108. package/src/utils/cookie.test.ts +180 -0
  109. package/src/utils/cx.test.ts +90 -0
  110. package/src/utils/index.test.ts +115 -0
  111. package/src/utils/speed-card-bg.test.ts +46 -0
  112. package/src/utils/utm.test.ts +359 -0
@@ -0,0 +1,268 @@
1
+ import React from "react";
2
+ import { SearchBlock } from "./index";
3
+ import { SearchBlockProps } from "./types";
4
+
5
+ import { fireEvent, render, screen } from "@testing-library/react";
6
+
7
+ // Mock dependencies
8
+ jest.mock("@shared/components/button", () => ({
9
+ Button: ({ children, onClick, type, className, ...rest }: any) => (
10
+ <button onClick={onClick} type={type} className={className} {...rest}>
11
+ {children}
12
+ </button>
13
+ ),
14
+ }));
15
+
16
+ jest.mock("@shared/components/input", () => ({
17
+ Input: ({
18
+ value,
19
+ onChange,
20
+ placeholder,
21
+ className,
22
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
23
+ containerClassName,
24
+ ...rest
25
+ }: any) => (
26
+ <input
27
+ value={value}
28
+ onChange={onChange}
29
+ placeholder={placeholder}
30
+ className={className}
31
+ data-testid="search-input"
32
+ {...rest}
33
+ />
34
+ ),
35
+ }));
36
+
37
+ jest.mock("@shared/components/material-icon", () => ({
38
+ MaterialIcon: ({ name, className }: any) => (
39
+ <span data-testid={`icon-${name}`} className={className}>
40
+ {name}
41
+ </span>
42
+ ),
43
+ }));
44
+
45
+ jest.mock("@shared/components/text", () => ({
46
+ Text: ({ as: Tag = "span", children, className }: any) => (
47
+ <Tag className={className}>{children}</Tag>
48
+ ),
49
+ }));
50
+
51
+ jest.mock("@shared/utils", () => ({
52
+ cx: (...args: any[]) => args.filter(Boolean).join(" "),
53
+ }));
54
+
55
+ const defaultProps: SearchBlockProps = {
56
+ title: "Search Our Plans",
57
+ backgroundColor: "white",
58
+ color: "dark",
59
+ };
60
+
61
+ describe("SearchBlock", () => {
62
+ describe("Rendering", () => {
63
+ it("renders with data-testid", () => {
64
+ render(<SearchBlock {...defaultProps} />);
65
+ expect(screen.getByTestId("search-block")).toBeInTheDocument();
66
+ });
67
+
68
+ it("renders title", () => {
69
+ render(<SearchBlock {...defaultProps} />);
70
+ expect(screen.getByText("Search Our Plans")).toBeInTheDocument();
71
+ });
72
+
73
+ it("renders with default anchorId", () => {
74
+ render(<SearchBlock {...defaultProps} />);
75
+ expect(screen.getByTestId("search-block")).toHaveAttribute(
76
+ "id",
77
+ "SearchBlock"
78
+ );
79
+ });
80
+
81
+ it("renders with custom anchorId", () => {
82
+ render(<SearchBlock {...defaultProps} anchorId="custom-search" />);
83
+ expect(screen.getByTestId("search-block")).toHaveAttribute(
84
+ "id",
85
+ "custom-search"
86
+ );
87
+ });
88
+
89
+ it("renders search input with placeholder", () => {
90
+ render(
91
+ <SearchBlock
92
+ {...defaultProps}
93
+ searchInputPlaceholder="Enter your address"
94
+ />
95
+ );
96
+ expect(screen.getByTestId("search-input")).toHaveAttribute(
97
+ "placeholder",
98
+ "Enter your address"
99
+ );
100
+ });
101
+
102
+ it("renders search icon button", () => {
103
+ render(<SearchBlock {...defaultProps} />);
104
+ expect(screen.getByTestId("icon-search")).toBeInTheDocument();
105
+ });
106
+
107
+ it("renders submit button with type submit", () => {
108
+ render(<SearchBlock {...defaultProps} />);
109
+ const submitBtn = screen.getByTestId("icon-search").closest("button");
110
+ expect(submitBtn).toHaveAttribute("type", "submit");
111
+ });
112
+ });
113
+
114
+ describe("Optional content", () => {
115
+ it("renders subtitle when provided", () => {
116
+ render(<SearchBlock {...defaultProps} subTitle="Find the best plan" />);
117
+ expect(screen.getByText("Find the best plan")).toBeInTheDocument();
118
+ });
119
+
120
+ it("does not render subtitle when not provided", () => {
121
+ render(<SearchBlock {...defaultProps} />);
122
+ expect(screen.queryByText("Find the best plan")).not.toBeInTheDocument();
123
+ });
124
+
125
+ it("renders searchDescription when provided", () => {
126
+ render(
127
+ <SearchBlock
128
+ {...defaultProps}
129
+ searchDescription="Enter your ZIP code"
130
+ />
131
+ );
132
+ expect(screen.getByText("Enter your ZIP code")).toBeInTheDocument();
133
+ });
134
+
135
+ it("does not render searchDescription when not provided", () => {
136
+ render(<SearchBlock {...defaultProps} />);
137
+ expect(screen.queryByText("Enter your ZIP code")).not.toBeInTheDocument();
138
+ });
139
+
140
+ it("renders caption ReactNode when provided", () => {
141
+ render(
142
+ <SearchBlock
143
+ {...defaultProps}
144
+ caption={<div data-testid="caption">Caption content</div>}
145
+ />
146
+ );
147
+ expect(screen.getByTestId("caption")).toBeInTheDocument();
148
+ });
149
+
150
+ it("does not render caption when not provided", () => {
151
+ render(<SearchBlock {...defaultProps} />);
152
+ expect(screen.queryByTestId("caption")).not.toBeInTheDocument();
153
+ });
154
+
155
+ it("renders basicCaption ReactNode when provided", () => {
156
+ render(
157
+ <SearchBlock
158
+ {...defaultProps}
159
+ basicCaption={<p data-testid="basic-caption">Basic caption</p>}
160
+ />
161
+ );
162
+ expect(screen.getByTestId("basic-caption")).toBeInTheDocument();
163
+ });
164
+ });
165
+
166
+ describe("Heading levels", () => {
167
+ it("renders title as h2 when enableHeading is false/undefined", () => {
168
+ render(<SearchBlock {...defaultProps} />);
169
+ const el = screen.getByText("Search Our Plans");
170
+ expect(el.tagName).toBe("H2");
171
+ });
172
+
173
+ it("renders title as h1 when enableHeading is true", () => {
174
+ render(<SearchBlock {...defaultProps} enableHeading={true} />);
175
+ const el = screen.getByText("Search Our Plans");
176
+ expect(el.tagName).toBe("H1");
177
+ });
178
+ });
179
+
180
+ describe("Background and color themes", () => {
181
+ it("applies supporting background class", () => {
182
+ render(<SearchBlock {...defaultProps} backgroundColor="supporting" />);
183
+ expect(screen.getByTestId("search-block")).toHaveClass(
184
+ "bg-bg-fill-brand-supporting"
185
+ );
186
+ });
187
+
188
+ it("applies white background class", () => {
189
+ render(<SearchBlock {...defaultProps} backgroundColor="white" />);
190
+ expect(screen.getByTestId("search-block")).toHaveClass("bg-bg");
191
+ });
192
+
193
+ it("applies brand background class", () => {
194
+ render(<SearchBlock {...defaultProps} backgroundColor="brand" />);
195
+ expect(screen.getByTestId("search-block")).toHaveClass(
196
+ "bg-bg-fill-brand"
197
+ );
198
+ });
199
+
200
+ it("applies dark text color", () => {
201
+ render(<SearchBlock {...defaultProps} color="dark" />);
202
+ expect(screen.getByTestId("search-block")).toHaveClass("text-text");
203
+ });
204
+
205
+ it("applies light (white) text color", () => {
206
+ render(<SearchBlock {...defaultProps} color="light" />);
207
+ expect(screen.getByTestId("search-block")).toHaveClass("text-white");
208
+ });
209
+ });
210
+
211
+ describe("Form submission", () => {
212
+ it("calls onSubmit with searchText on form submit", () => {
213
+ const mockSubmit = jest.fn();
214
+ render(<SearchBlock {...defaultProps} onSubmit={mockSubmit} />);
215
+ const input = screen.getByTestId("search-input");
216
+ fireEvent.change(input, { target: { value: "90210" } });
217
+ const form = input.closest("form")!;
218
+ fireEvent.submit(form);
219
+ expect(mockSubmit).toHaveBeenCalledWith({ searchText: "90210" });
220
+ });
221
+
222
+ it("calls onSubmit with empty string when no text entered", () => {
223
+ const mockSubmit = jest.fn();
224
+ render(<SearchBlock {...defaultProps} onSubmit={mockSubmit} />);
225
+ const form = screen.getByTestId("search-input").closest("form")!;
226
+ fireEvent.submit(form);
227
+ expect(mockSubmit).toHaveBeenCalledWith({ searchText: "" });
228
+ });
229
+
230
+ it("does not throw when onSubmit is not provided", () => {
231
+ render(<SearchBlock {...defaultProps} />);
232
+ const form = screen.getByTestId("search-input").closest("form")!;
233
+ expect(() => fireEvent.submit(form)).not.toThrow();
234
+ });
235
+
236
+ it("prevents default form submission", () => {
237
+ const mockSubmit = jest.fn();
238
+ render(<SearchBlock {...defaultProps} onSubmit={mockSubmit} />);
239
+ const form = screen.getByTestId("search-input").closest("form")!;
240
+ const submitEvent = new Event("submit", {
241
+ bubbles: true,
242
+ cancelable: true,
243
+ });
244
+ const prevented = !form.dispatchEvent(submitEvent);
245
+ expect(prevented).toBe(true);
246
+ });
247
+ });
248
+
249
+ describe("Input interaction", () => {
250
+ it("updates input value on change", () => {
251
+ render(<SearchBlock {...defaultProps} />);
252
+ const input = screen.getByTestId("search-input") as HTMLInputElement;
253
+ fireEvent.change(input, { target: { value: "test query" } });
254
+ expect(input.value).toBe("test query");
255
+ });
256
+
257
+ it("submits the latest typed value", () => {
258
+ const mockSubmit = jest.fn();
259
+ render(<SearchBlock {...defaultProps} onSubmit={mockSubmit} />);
260
+ const input = screen.getByTestId("search-input");
261
+ fireEvent.change(input, { target: { value: "first" } });
262
+ fireEvent.change(input, { target: { value: "second" } });
263
+ const form = input.closest("form")!;
264
+ fireEvent.submit(form);
265
+ expect(mockSubmit).toHaveBeenCalledWith({ searchText: "second" });
266
+ });
267
+ });
268
+ });
@@ -0,0 +1,284 @@
1
+ import React from "react";
2
+ import { ShapeBackgroundWrapper } from "./index";
3
+
4
+ import { render, screen } from "@testing-library/react";
5
+
6
+ describe("ShapeBackgroundWrapper", () => {
7
+ describe("Rendering with show=true (default)", () => {
8
+ it("renders children inside shape-bg-content wrapper", () => {
9
+ render(
10
+ <ShapeBackgroundWrapper background="white">
11
+ <p>Child content</p>
12
+ </ShapeBackgroundWrapper>
13
+ );
14
+ expect(screen.getByText("Child content")).toBeInTheDocument();
15
+ expect(
16
+ screen.getByText("Child content").closest(".shape-bg-content")
17
+ ).toBeInTheDocument();
18
+ });
19
+
20
+ it("renders the outer container with shape-bg class", () => {
21
+ const { container } = render(
22
+ <ShapeBackgroundWrapper background="white">
23
+ <span>Test</span>
24
+ </ShapeBackgroundWrapper>
25
+ );
26
+ expect(container.querySelector(".shape-bg")).toBeInTheDocument();
27
+ });
28
+
29
+ it("renders content div with z-[1] for layering above SVG", () => {
30
+ const { container } = render(
31
+ <ShapeBackgroundWrapper background="white">
32
+ <span>Layered</span>
33
+ </ShapeBackgroundWrapper>
34
+ );
35
+ const content = container.querySelector(".shape-bg-content");
36
+ expect(content).toHaveClass("relative", "z-[1]");
37
+ });
38
+ });
39
+
40
+ describe("Rendering with show=false", () => {
41
+ it("renders children without shape-bg wrapper", () => {
42
+ const { container } = render(
43
+ <ShapeBackgroundWrapper show={false} background="navy">
44
+ <p>Hidden shape</p>
45
+ </ShapeBackgroundWrapper>
46
+ );
47
+ expect(screen.getByText("Hidden shape")).toBeInTheDocument();
48
+ expect(container.querySelector(".shape-bg")).not.toBeInTheDocument();
49
+ });
50
+
51
+ it("does not render any SVG when show is false", () => {
52
+ const { container } = render(
53
+ <ShapeBackgroundWrapper show={false} path="path1" background="white">
54
+ <span>No SVG</span>
55
+ </ShapeBackgroundWrapper>
56
+ );
57
+ expect(container.querySelector("svg")).not.toBeInTheDocument();
58
+ });
59
+
60
+ it("applies className when show is false", () => {
61
+ const { container } = render(
62
+ <ShapeBackgroundWrapper show={false} className="custom-class">
63
+ <span>Styled</span>
64
+ </ShapeBackgroundWrapper>
65
+ );
66
+ expect(container.firstChild).toHaveClass("custom-class");
67
+ });
68
+
69
+ it("applies custom background style when show is false and background is non-theme", () => {
70
+ const { container } = render(
71
+ <ShapeBackgroundWrapper
72
+ show={false}
73
+ background="linear-gradient(red, blue)"
74
+ >
75
+ <span>Gradient</span>
76
+ </ShapeBackgroundWrapper>
77
+ );
78
+ expect(container.firstChild).toHaveStyle({
79
+ background: "linear-gradient(red, blue)",
80
+ });
81
+ });
82
+ });
83
+
84
+ describe("SVG path rendering", () => {
85
+ it("renders path1 SVG when path='path1'", () => {
86
+ const { container } = render(
87
+ <ShapeBackgroundWrapper path="path1" background="white">
88
+ <span>Path1</span>
89
+ </ShapeBackgroundWrapper>
90
+ );
91
+ const svgs = container.querySelectorAll("svg");
92
+ expect(svgs).toHaveLength(1);
93
+ expect(svgs[0]).toHaveAttribute("width", "2262");
94
+ expect(svgs[0]).toHaveAttribute("height", "4359");
95
+ });
96
+
97
+ it("renders path2 SVG by default", () => {
98
+ const { container } = render(
99
+ <ShapeBackgroundWrapper background="white">
100
+ <span>Default path</span>
101
+ </ShapeBackgroundWrapper>
102
+ );
103
+ const svgs = container.querySelectorAll("svg");
104
+ expect(svgs).toHaveLength(1);
105
+ expect(svgs[0]).toHaveAttribute("width", "3739");
106
+ expect(svgs[0]).toHaveAttribute("height", "3666");
107
+ });
108
+
109
+ it("renders path3 SVG when path='path3'", () => {
110
+ const { container } = render(
111
+ <ShapeBackgroundWrapper path="path3" background="white">
112
+ <span>Path3</span>
113
+ </ShapeBackgroundWrapper>
114
+ );
115
+ const svgs = container.querySelectorAll("svg");
116
+ expect(svgs).toHaveLength(1);
117
+ expect(svgs[0]).toHaveAttribute("width", "3044");
118
+ expect(svgs[0]).toHaveAttribute("height", "3248");
119
+ });
120
+
121
+ it("renders path4 SVG when path='path4'", () => {
122
+ const { container } = render(
123
+ <ShapeBackgroundWrapper path="path4" background="white">
124
+ <span>Path4</span>
125
+ </ShapeBackgroundWrapper>
126
+ );
127
+ const svgs = container.querySelectorAll("svg");
128
+ expect(svgs).toHaveLength(1);
129
+ expect(svgs[0]).toHaveAttribute("width", "3739");
130
+ expect(svgs[0]).toHaveAttribute("height", "3666");
131
+ });
132
+
133
+ it("does not render SVG for unrecognized path value", () => {
134
+ const { container } = render(
135
+ <ShapeBackgroundWrapper path={"path5" as any} background="white">
136
+ <span>Unknown</span>
137
+ </ShapeBackgroundWrapper>
138
+ );
139
+ expect(container.querySelector("svg")).not.toBeInTheDocument();
140
+ });
141
+
142
+ it("marks SVG with aria-hidden and focusable=false", () => {
143
+ const { container } = render(
144
+ <ShapeBackgroundWrapper path="path1" background="white">
145
+ <span>Accessible</span>
146
+ </ShapeBackgroundWrapper>
147
+ );
148
+ const svg = container.querySelector("svg")!;
149
+ expect(svg).toHaveAttribute("aria-hidden", "true");
150
+ expect(svg).toHaveAttribute("focusable", "false");
151
+ });
152
+
153
+ it("applies pointer-events-none to SVG", () => {
154
+ const { container } = render(
155
+ <ShapeBackgroundWrapper path="path2" background="white">
156
+ <span>Non-interactive</span>
157
+ </ShapeBackgroundWrapper>
158
+ );
159
+ const svg = container.querySelector("svg")!;
160
+ expect(svg).toHaveClass("pointer-events-none");
161
+ });
162
+ });
163
+
164
+ describe("Background themes", () => {
165
+ it.each([
166
+ ["blue", "bg-[#07B2E2]"],
167
+ ["green", "bg-[#26B170]"],
168
+ ["yellow", "bg-[#F5FF1E]"],
169
+ ["purple", "bg-[#931D69]"],
170
+ ["white", "bg-white"],
171
+ ["navy", "bg-[#00002D]"],
172
+ ] as const)("applies %s background class", (bg, expectedClass) => {
173
+ const { container } = render(
174
+ <ShapeBackgroundWrapper background={bg}>
175
+ <span>BG Test</span>
176
+ </ShapeBackgroundWrapper>
177
+ );
178
+ const outer = container.querySelector(".shape-bg")!;
179
+ expect(outer.className).toContain(expectedClass);
180
+ });
181
+
182
+ it("applies inline style for custom (non-theme) background", () => {
183
+ const { container } = render(
184
+ <ShapeBackgroundWrapper background="#FF5733">
185
+ <span>Custom BG</span>
186
+ </ShapeBackgroundWrapper>
187
+ );
188
+ const outer = container.querySelector(".shape-bg") as HTMLElement;
189
+ expect(outer.style.background).toBe("rgb(255, 87, 51)");
190
+ });
191
+
192
+ it("falls back to bg-white when no background provided", () => {
193
+ const { container } = render(
194
+ <ShapeBackgroundWrapper>
195
+ <span>No BG</span>
196
+ </ShapeBackgroundWrapper>
197
+ );
198
+ const outer = container.querySelector(".shape-bg")!;
199
+ expect(outer.className).toContain("bg-white");
200
+ });
201
+ });
202
+
203
+ describe("Fill color (SVG)", () => {
204
+ it.each([
205
+ ["blue", "text-[#07B2E2]"],
206
+ ["green", "text-[#26B170]"],
207
+ ["yellow", "text-[#F5FF1E]"],
208
+ ["purple", "text-[#931D69]"],
209
+ ["white", "text-white"],
210
+ ["navy", "text-[#00002D]"],
211
+ ] as const)("applies %s fill color to SVG", (fill, expectedClass) => {
212
+ const { container } = render(
213
+ <ShapeBackgroundWrapper fill={fill} background="white">
214
+ <span>Fill</span>
215
+ </ShapeBackgroundWrapper>
216
+ );
217
+ const svg = container.querySelector("svg")!;
218
+ expect(svg.className.baseVal).toContain(expectedClass);
219
+ });
220
+
221
+ it("defaults to yellow fill when fill not specified", () => {
222
+ const { container } = render(
223
+ <ShapeBackgroundWrapper background="white">
224
+ <span>Default fill</span>
225
+ </ShapeBackgroundWrapper>
226
+ );
227
+ const svg = container.querySelector("svg")!;
228
+ expect(svg.className.baseVal).toContain("text-[#F5FF1E]");
229
+ });
230
+
231
+ it("falls back to yellow for unrecognized fill value", () => {
232
+ const { container } = render(
233
+ <ShapeBackgroundWrapper fill="magenta" background="white">
234
+ <span>Unknown fill</span>
235
+ </ShapeBackgroundWrapper>
236
+ );
237
+ const svg = container.querySelector("svg")!;
238
+ expect(svg.className.baseVal).toContain("text-[#F5FF1E]");
239
+ });
240
+ });
241
+
242
+ describe("className prop", () => {
243
+ it("applies custom className to shape-bg wrapper", () => {
244
+ const { container } = render(
245
+ <ShapeBackgroundWrapper className="extra-class" background="white">
246
+ <span>Classy</span>
247
+ </ShapeBackgroundWrapper>
248
+ );
249
+ const outer = container.querySelector(".shape-bg")!;
250
+ expect(outer).toHaveClass("extra-class");
251
+ });
252
+
253
+ it("handles undefined className without errors", () => {
254
+ const { container } = render(
255
+ <ShapeBackgroundWrapper background="white">
256
+ <span>No class</span>
257
+ </ShapeBackgroundWrapper>
258
+ );
259
+ expect(container.querySelector(".shape-bg")).toBeInTheDocument();
260
+ });
261
+ });
262
+
263
+ describe("maxFit prop", () => {
264
+ it("applies max-content class when maxFit is true", () => {
265
+ const { container } = render(
266
+ <ShapeBackgroundWrapper maxFit={true} background="white">
267
+ <span>Max fit</span>
268
+ </ShapeBackgroundWrapper>
269
+ );
270
+ const outer = container.querySelector(".shape-bg")!;
271
+ expect(outer.className).toContain("max-content");
272
+ });
273
+
274
+ it("does not apply max-content when maxFit is false/undefined", () => {
275
+ const { container } = render(
276
+ <ShapeBackgroundWrapper background="white">
277
+ <span>No max fit</span>
278
+ </ShapeBackgroundWrapper>
279
+ );
280
+ const outer = container.querySelector(".shape-bg")!;
281
+ expect(outer.className).not.toContain("max-content");
282
+ });
283
+ });
284
+ });
@@ -0,0 +1,36 @@
1
+ import React from "react";
2
+ import { Text } from "./index";
3
+
4
+ import { render, screen } from "@testing-library/react";
5
+
6
+ // Mock dependencies
7
+ jest.mock("@shared/components/text", () => ({
8
+ Text: ({ children }: { children: React.ReactNode }) => (
9
+ <span data-testid="text-component">{children}</span>
10
+ ),
11
+ }));
12
+
13
+ describe("Text (Contentful Block)", () => {
14
+ describe("Rendering", () => {
15
+ it("renders the Text Block placeholder text", () => {
16
+ render(<Text fields={{}} />);
17
+ expect(screen.getByText("Text Block")).toBeInTheDocument();
18
+ });
19
+
20
+ it("renders inside a div wrapper", () => {
21
+ const { container } = render(<Text fields={{}} />);
22
+ expect(container.firstChild?.nodeName).toBe("DIV");
23
+ });
24
+
25
+ it("uses the shared TextComponent", () => {
26
+ render(<Text fields={{}} />);
27
+ expect(screen.getByTestId("text-component")).toBeInTheDocument();
28
+ });
29
+
30
+ it("renders consistently with empty fields object", () => {
31
+ const { container: first } = render(<Text fields={{}} />);
32
+ const { container: second } = render(<Text fields={{}} />);
33
+ expect(first.innerHTML).toBe(second.innerHTML);
34
+ });
35
+ });
36
+ });
@@ -0,0 +1,45 @@
1
+ import * as ContentfulExports from "./index";
2
+
3
+ describe("contentful barrel exports", () => {
4
+ it("exports all expected contentful blocks", () => {
5
+ expect(ContentfulExports.Accordion).toBeDefined();
6
+ expect(ContentfulExports.Callout).toBeDefined();
7
+ expect(ContentfulExports.Cards).toBeDefined();
8
+ expect(ContentfulExports.SimpleCard).toBeDefined();
9
+ expect(ContentfulExports.Carousel).toBeDefined();
10
+ expect(ContentfulExports.FloatingBanner).toBeDefined();
11
+ expect(ContentfulExports.Footer).toBeDefined();
12
+ expect(ContentfulExports.ImagePromoBar).toBeDefined();
13
+ expect(ContentfulExports.Navigation).toBeDefined();
14
+ expect(ContentfulExports.PrimaryHero).toBeDefined();
15
+ expect(ContentfulExports.Text).toBeDefined();
16
+ expect(ContentfulExports.Button).toBeDefined();
17
+ expect(ContentfulExports.Modal).toBeDefined();
18
+ expect(ContentfulExports.ShapeBackgroundWrapper).toBeDefined();
19
+ expect(ContentfulExports.CtaCallout).toBeDefined();
20
+ expect(ContentfulExports.FindKinetic).toBeDefined();
21
+ expect(ContentfulExports.ComparisonTable).toBeDefined();
22
+ expect(ContentfulExports.TestimonialCard).toBeDefined();
23
+ expect(ContentfulExports.ProductCard).toBeDefined();
24
+ expect(ContentfulExports.BlogCard).toBeDefined();
25
+ expect(ContentfulExports.BlogGrid).toBeDefined();
26
+ expect(ContentfulExports.BlogGridBase).toBeDefined();
27
+ expect(ContentfulExports.BreadcrumbNavigation).toBeDefined();
28
+ expect(ContentfulExports.SearchBlock).toBeDefined();
29
+ expect(ContentfulExports.AddressInputBanner).toBeDefined();
30
+ expect(ContentfulExports.DynamicTabs).toBeDefined();
31
+ expect(ContentfulExports.AnchoredBottomBanner).toBeDefined();
32
+ expect(ContentfulExports.CookieBanner).toBeDefined();
33
+ expect(ContentfulExports.EmailInputBlock).toBeDefined();
34
+ expect(ContentfulExports.CartRetentionBanner).toBeDefined();
35
+ });
36
+
37
+ it("exports contentful rich text utilities", () => {
38
+ expect(ContentfulExports.toDocument).toBeDefined();
39
+ expect(ContentfulExports.renderContentfulRichText).toBeDefined();
40
+ expect(ContentfulExports.useContentfulRichText).toBeDefined();
41
+ expect(ContentfulExports.renderContentfulRichTextTable).toBeDefined();
42
+ expect(ContentfulExports.useProcessedChecklist).toBeDefined();
43
+ expect(ContentfulExports.label1BoldOptions).toBeDefined();
44
+ });
45
+ });
@@ -0,0 +1,25 @@
1
+ /* eslint-env jest */
2
+ /**
3
+ * Mock: utils/contentful/to-document.ts
4
+ *
5
+ * Provides a mock `toDocument` that returns null by default.
6
+ *
7
+ * Usage:
8
+ * import { mockToDocumentUtil, resetToDocumentMocks } from "@shared/global-mocks/contentful/to-document";
9
+ * mockToDocumentUtil();
10
+ * afterEach(resetToDocumentMocks);
11
+ */
12
+
13
+ export const mockToDocument = jest.fn().mockReturnValue(null);
14
+
15
+ export function mockToDocumentUtil() {
16
+ jest.mock("@shared/utils/contentful/to-document", () => ({
17
+ toDocument: mockToDocument,
18
+ }));
19
+
20
+ return { mockToDocument };
21
+ }
22
+
23
+ export function resetToDocumentMocks() {
24
+ mockToDocument.mockReset().mockReturnValue(null);
25
+ }