@windstream/react-shared-components 0.2.79 → 0.2.81
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/contentful/index.d.ts +17 -1
- package/dist/contentful/index.esm.js +2 -2
- package/dist/contentful/index.esm.js.map +1 -1
- package/dist/contentful/index.js +2 -2
- package/dist/contentful/index.js.map +1 -1
- package/dist/core.d.ts +1 -1
- package/dist/index.d.ts +1 -1
- package/package.json +1 -1
- package/src/contentful/blocks/address-input-banner/index.test.tsx +13 -0
- package/src/contentful/blocks/address-input-banner/index.tsx +5 -1
- package/src/contentful/blocks/address-input-banner/types.ts +3 -0
- package/src/contentful/blocks/button/index.test.tsx +45 -2
- package/src/contentful/blocks/button/index.tsx +13 -1
- package/src/contentful/blocks/button/types.ts +2 -0
- package/src/contentful/blocks/cards/product-card/index.tsx +11 -3
- package/src/contentful/blocks/cards/product-card/types.ts +11 -0
- package/src/contentful/blocks/carousel/helper.tsx +1 -0
- package/src/contentful/blocks/carousel/index.tsx +8 -1
- package/src/contentful/blocks/carousel/types.ts +15 -0
- package/src/contentful/blocks/footer/index.test.tsx +65 -2
- package/src/contentful/blocks/footer/index.tsx +14 -4
- package/src/contentful/blocks/footer/types.ts +4 -0
- package/src/contentful/blocks/navigation/desktop-link-groups.tsx/index.test.tsx +26 -2
- package/src/contentful/blocks/navigation/desktop-link-groups.tsx/index.tsx +8 -2
- package/src/contentful/blocks/navigation/index.test.tsx +40 -2
- package/src/contentful/blocks/navigation/index.tsx +25 -5
- package/src/contentful/blocks/navigation/mobile-link-groups.tsx/index.test.tsx +14 -0
- package/src/contentful/blocks/navigation/mobile-link-groups.tsx/index.tsx +4 -1
- package/src/contentful/blocks/navigation/types.ts +5 -0
- package/src/types/micro-components.ts +6 -0
|
@@ -84,9 +84,16 @@ jest.mock("@shared/components/material-icon", () => ({
|
|
|
84
84
|
}));
|
|
85
85
|
|
|
86
86
|
jest.mock("@shared/components/next-image", () => ({
|
|
87
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
88
87
|
NextImage: ({ src, alt, onClick, ...rest }: any) => (
|
|
89
|
-
<img
|
|
88
|
+
<img
|
|
89
|
+
data-testid="next-image"
|
|
90
|
+
src={src}
|
|
91
|
+
alt={alt}
|
|
92
|
+
onClick={onClick}
|
|
93
|
+
{...Object.fromEntries(
|
|
94
|
+
Object.entries(rest).filter(([k]) => k.startsWith("data-"))
|
|
95
|
+
)}
|
|
96
|
+
/>
|
|
90
97
|
),
|
|
91
98
|
}));
|
|
92
99
|
|
|
@@ -149,6 +156,37 @@ const mobileMenuProps: NavigationProps = {
|
|
|
149
156
|
describe("Navigation", () => {
|
|
150
157
|
beforeEach(() => jest.clearAllMocks());
|
|
151
158
|
|
|
159
|
+
describe("fieldProps", () => {
|
|
160
|
+
const tag = (field: string) => ({ "data-contentful-field-id": field });
|
|
161
|
+
|
|
162
|
+
it("spreads logo fieldProps onto the desktop and mobile logo links", () => {
|
|
163
|
+
const { container } = render(
|
|
164
|
+
<Navigation {...defaultProps} fieldProps={{ logo: tag("logo") }} />
|
|
165
|
+
);
|
|
166
|
+
const tagged = container.querySelectorAll(
|
|
167
|
+
'a[data-contentful-field-id="logo"]'
|
|
168
|
+
);
|
|
169
|
+
expect(tagged).toHaveLength(2);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it("spreads searchBarIcon fieldProps onto the desktop search icon", () => {
|
|
173
|
+
const { container } = render(
|
|
174
|
+
<Navigation
|
|
175
|
+
{...defaultProps}
|
|
176
|
+
fieldProps={{ searchBarIcon: tag("searchBarIcon") }}
|
|
177
|
+
/>
|
|
178
|
+
);
|
|
179
|
+
expect(
|
|
180
|
+
container.querySelector('img[data-contentful-field-id="searchBarIcon"]')
|
|
181
|
+
).not.toBeNull();
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it("renders no tagging attributes when fieldProps is omitted", () => {
|
|
185
|
+
const { container } = render(<Navigation {...defaultProps} />);
|
|
186
|
+
expect(container.querySelector("[data-contentful-field-id]")).toBeNull();
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
|
|
152
190
|
describe("enableHamburgerNavigationOnDesktop (desktop hamburger A/B variant)", () => {
|
|
153
191
|
it("keeps current behavior when the flag is off", () => {
|
|
154
192
|
const { container } = render(<Navigation {...defaultProps} />);
|
|
@@ -46,6 +46,7 @@ export const Navigation: React.FC<NavigationProps> = props => {
|
|
|
46
46
|
callNowCtaIcon,
|
|
47
47
|
navigationBackgroundColor,
|
|
48
48
|
navigationLinkColor,
|
|
49
|
+
fieldProps,
|
|
49
50
|
} = props;
|
|
50
51
|
|
|
51
52
|
const logoUrl =
|
|
@@ -94,6 +95,7 @@ export const Navigation: React.FC<NavigationProps> = props => {
|
|
|
94
95
|
width={24}
|
|
95
96
|
height={24}
|
|
96
97
|
className="mr-2 self-center rounded-full"
|
|
98
|
+
{...fieldProps?.callNowCtaIcon}
|
|
97
99
|
/>
|
|
98
100
|
) : null}
|
|
99
101
|
<Text as="span" className="text-secondary-navy500 footnote">
|
|
@@ -159,7 +161,18 @@ export const Navigation: React.FC<NavigationProps> = props => {
|
|
|
159
161
|
<CallButton
|
|
160
162
|
className="border-none"
|
|
161
163
|
href={invocaPhoneNumberLink}
|
|
162
|
-
prefix={
|
|
164
|
+
prefix={
|
|
165
|
+
callNowCtaText ? (
|
|
166
|
+
<span
|
|
167
|
+
className="text-sm font-normal text-text"
|
|
168
|
+
{...fieldProps?.callNowCtaText}
|
|
169
|
+
>
|
|
170
|
+
{callNowCtaText}
|
|
171
|
+
</span>
|
|
172
|
+
) : (
|
|
173
|
+
callNowCtaText
|
|
174
|
+
)
|
|
175
|
+
}
|
|
163
176
|
onClick={onCallClickDesktop}
|
|
164
177
|
>
|
|
165
178
|
<Text className="body3 text-text">
|
|
@@ -210,7 +223,7 @@ export const Navigation: React.FC<NavigationProps> = props => {
|
|
|
210
223
|
>
|
|
211
224
|
<div>
|
|
212
225
|
{logoUrl ? (
|
|
213
|
-
<Link href="/" className="flex">
|
|
226
|
+
<Link href="/" className="flex" {...fieldProps?.logo}>
|
|
214
227
|
<NextImage
|
|
215
228
|
src={logoUrl}
|
|
216
229
|
alt="Kinetic business logo"
|
|
@@ -265,7 +278,7 @@ export const Navigation: React.FC<NavigationProps> = props => {
|
|
|
265
278
|
<div className="mx-auto flex h-14 max-w-120 items-center justify-between">
|
|
266
279
|
<div className="flex h-full">
|
|
267
280
|
{logoUrl ? (
|
|
268
|
-
<Link href="/" className="flex">
|
|
281
|
+
<Link href="/" className="flex" {...fieldProps?.logo}>
|
|
269
282
|
<NextImage
|
|
270
283
|
src={logoUrl}
|
|
271
284
|
alt="Kinetic business logo"
|
|
@@ -302,6 +315,7 @@ export const Navigation: React.FC<NavigationProps> = props => {
|
|
|
302
315
|
: searchBarIcon?.url || ""
|
|
303
316
|
}
|
|
304
317
|
onSearch={onSearch}
|
|
318
|
+
iconFieldProps={fieldProps?.searchBarIcon}
|
|
305
319
|
/>
|
|
306
320
|
) : null}
|
|
307
321
|
{supportNavigationLinks?.map((links, index) => {
|
|
@@ -448,6 +462,7 @@ const MobileMenu = (props: NavigationProps) => {
|
|
|
448
462
|
: props.searchBarIcon?.url || ""
|
|
449
463
|
}
|
|
450
464
|
onSearch={props.onSearch || (() => {})}
|
|
465
|
+
iconFieldProps={props.fieldProps?.searchBarIcon}
|
|
451
466
|
/>
|
|
452
467
|
) : null}
|
|
453
468
|
<div
|
|
@@ -510,8 +525,10 @@ const MobileSearchInput = (props: {
|
|
|
510
525
|
onSearch: (query: string) => void;
|
|
511
526
|
isMenuOpen: boolean;
|
|
512
527
|
searchBarIconURL: string;
|
|
528
|
+
iconFieldProps?: Record<string, unknown>;
|
|
513
529
|
}) => {
|
|
514
|
-
const { closeMenu, onSearch, isMenuOpen, searchBarIconURL } =
|
|
530
|
+
const { closeMenu, onSearch, isMenuOpen, searchBarIconURL, iconFieldProps } =
|
|
531
|
+
props;
|
|
515
532
|
const [searchValue, setSearchValue] = React.useState("");
|
|
516
533
|
const searchInputRef = React.useRef<HTMLInputElement>(null);
|
|
517
534
|
|
|
@@ -541,6 +558,7 @@ const MobileSearchInput = (props: {
|
|
|
541
558
|
alt="Search icon"
|
|
542
559
|
role="button"
|
|
543
560
|
onClick={redirectToSearchResults}
|
|
561
|
+
{...iconFieldProps}
|
|
544
562
|
/>
|
|
545
563
|
<div className="flex-grow">
|
|
546
564
|
<Input
|
|
@@ -561,8 +579,9 @@ const MobileSearchInput = (props: {
|
|
|
561
579
|
const DesktopSearchInput = (props: {
|
|
562
580
|
searchBarIconURL: string;
|
|
563
581
|
onSearch: (query: string) => void;
|
|
582
|
+
iconFieldProps?: Record<string, unknown>;
|
|
564
583
|
}) => {
|
|
565
|
-
const { searchBarIconURL, onSearch } = props;
|
|
584
|
+
const { searchBarIconURL, onSearch, iconFieldProps } = props;
|
|
566
585
|
const [searchValue, setSearchValue] = React.useState("");
|
|
567
586
|
const searchInputRef = React.useRef<HTMLInputElement>(null);
|
|
568
587
|
|
|
@@ -585,6 +604,7 @@ const DesktopSearchInput = (props: {
|
|
|
585
604
|
alt="Search icon"
|
|
586
605
|
role="button"
|
|
587
606
|
onClick={redirectToSearchResults}
|
|
607
|
+
{...iconFieldProps}
|
|
588
608
|
/>
|
|
589
609
|
{/* Input */}
|
|
590
610
|
<Input
|
|
@@ -28,6 +28,7 @@ jest.mock("@shared/contentful/blocks/button", () => ({
|
|
|
28
28
|
className={buttonClassName || linkClassName}
|
|
29
29
|
onClick={onClick}
|
|
30
30
|
aria-expanded={rest["aria-expanded"]}
|
|
31
|
+
data-field-props={JSON.stringify(rest.fieldProps ?? null)}
|
|
31
32
|
>
|
|
32
33
|
{children || rest.buttonLabel}
|
|
33
34
|
</button>
|
|
@@ -35,6 +36,19 @@ jest.mock("@shared/contentful/blocks/button", () => ({
|
|
|
35
36
|
}));
|
|
36
37
|
|
|
37
38
|
describe("MobileLinkGroups", () => {
|
|
39
|
+
it("passes the group's title fieldProps to the toggle button", () => {
|
|
40
|
+
const link = {
|
|
41
|
+
title: "Shop",
|
|
42
|
+
items: { items: [] },
|
|
43
|
+
fieldProps: { title: { "data-contentful-field-id": "title" } },
|
|
44
|
+
};
|
|
45
|
+
render(<MobileLinkGroups link={link as any} />);
|
|
46
|
+
expect(screen.getByTestId("button")).toHaveAttribute(
|
|
47
|
+
"data-field-props",
|
|
48
|
+
JSON.stringify({ "data-contentful-field-id": "title" })
|
|
49
|
+
);
|
|
50
|
+
});
|
|
51
|
+
|
|
38
52
|
it("renders nothing when link is undefined", () => {
|
|
39
53
|
const { container } = render(<MobileLinkGroups link={undefined} />);
|
|
40
54
|
expect(container.innerHTML).toBe("");
|
|
@@ -3,11 +3,13 @@ import React from "react";
|
|
|
3
3
|
import { MaterialIcon } from "@shared/components/material-icon";
|
|
4
4
|
import { Button } from "@shared/contentful/blocks/button";
|
|
5
5
|
import { ButtonProps as ContentfulButtonProps } from "@shared/contentful/blocks/button/types";
|
|
6
|
+
import type { FieldProps } from "@shared/types/field-props";
|
|
6
7
|
|
|
7
8
|
type ComponentButtonGroup = {
|
|
8
9
|
anchorId?: string | null;
|
|
9
10
|
title?: string | null;
|
|
10
11
|
items?: { items?: ContentfulButtonProps[] | null } | null;
|
|
12
|
+
fieldProps?: FieldProps<"title">;
|
|
11
13
|
};
|
|
12
14
|
|
|
13
15
|
type Link = ContentfulButtonProps | ComponentButtonGroup;
|
|
@@ -38,7 +40,7 @@ export const MobileLinkGroups: React.FC<LinkGroupsProps> = ({ link }) => {
|
|
|
38
40
|
}
|
|
39
41
|
|
|
40
42
|
// Group
|
|
41
|
-
const { anchorId, title, items } = link;
|
|
43
|
+
const { anchorId, title, items, fieldProps } = link;
|
|
42
44
|
const subMenu = Array.isArray(items?.items) ? items!.items! : [];
|
|
43
45
|
|
|
44
46
|
return (
|
|
@@ -49,6 +51,7 @@ export const MobileLinkGroups: React.FC<LinkGroupsProps> = ({ link }) => {
|
|
|
49
51
|
buttonClassName=" label3 flex items-center w-full h-11 px-4"
|
|
50
52
|
key={anchorId}
|
|
51
53
|
showButtonAs="unstyled"
|
|
54
|
+
fieldProps={fieldProps?.title}
|
|
52
55
|
>
|
|
53
56
|
{title ?? null}
|
|
54
57
|
<MaterialIcon
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
|
|
3
|
+
import type { FieldProps } from "@shared/types/field-props";
|
|
3
4
|
import { Asset, Button, ButtonGroup } from "@shared/types/micro-components";
|
|
4
5
|
|
|
5
6
|
type Color = string;
|
|
@@ -69,6 +70,10 @@ export interface Navigation {
|
|
|
69
70
|
/** When true, renders the mobile hamburger navigation in the desktop
|
|
70
71
|
* viewport and hides the inline desktop nav. Defaults to false. */
|
|
71
72
|
enableHamburgerNavigationOnDesktop?: boolean;
|
|
73
|
+
/** Extra DOM attribute props, spread onto the respective rendered elements. */
|
|
74
|
+
fieldProps?: FieldProps<
|
|
75
|
+
"logo" | "searchBarIcon" | "callNowCtaText" | "callNowCtaIcon"
|
|
76
|
+
>;
|
|
72
77
|
}
|
|
73
78
|
|
|
74
79
|
export type NavigationProps = Navigation;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { FieldProps } from "./field-props";
|
|
2
|
+
|
|
1
3
|
import { ButtonProps } from "@shared/contentful";
|
|
2
4
|
|
|
3
5
|
export interface Asset {
|
|
@@ -29,6 +31,8 @@ export interface Button {
|
|
|
29
31
|
clickToOpen?: "accordion" | "modal" | "tab" | null;
|
|
30
32
|
tabmodalNameToOpen?: string | null;
|
|
31
33
|
preDefinedFunctionExecution?: "check availability" | null;
|
|
34
|
+
/** Extra DOM attribute props, spread onto the rendered link/button element. */
|
|
35
|
+
fieldProps?: Record<string, unknown>;
|
|
32
36
|
}
|
|
33
37
|
|
|
34
38
|
export interface ButtonGroup {
|
|
@@ -38,6 +42,8 @@ export interface ButtonGroup {
|
|
|
38
42
|
itemsCollection?: {
|
|
39
43
|
items: Button[];
|
|
40
44
|
};
|
|
45
|
+
/** Extra DOM attribute props, spread onto the group's title element. */
|
|
46
|
+
fieldProps?: FieldProps<"title">;
|
|
41
47
|
}
|
|
42
48
|
export interface Card {
|
|
43
49
|
anchorId?: string;
|