@windstream/react-shared-components 0.2.80 → 0.2.82

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 (28) hide show
  1. package/dist/contentful/index.d.ts +11 -1
  2. package/dist/contentful/index.esm.js +2 -2
  3. package/dist/contentful/index.esm.js.map +1 -1
  4. package/dist/contentful/index.js +2 -2
  5. package/dist/contentful/index.js.map +1 -1
  6. package/dist/core.d.ts +1 -1
  7. package/dist/index.d.ts +1 -1
  8. package/package.json +1 -1
  9. package/src/contentful/blocks/address-input-banner/index.test.tsx +13 -0
  10. package/src/contentful/blocks/address-input-banner/index.tsx +5 -1
  11. package/src/contentful/blocks/address-input-banner/types.ts +3 -0
  12. package/src/contentful/blocks/anchored-bottom-banner/index.test.tsx +35 -0
  13. package/src/contentful/blocks/anchored-bottom-banner/index.tsx +32 -16
  14. package/src/contentful/blocks/anchored-bottom-banner/types.ts +18 -1
  15. package/src/contentful/blocks/button/index.test.tsx +45 -2
  16. package/src/contentful/blocks/button/index.tsx +13 -1
  17. package/src/contentful/blocks/button/types.ts +2 -0
  18. package/src/contentful/blocks/footer/index.test.tsx +65 -2
  19. package/src/contentful/blocks/footer/index.tsx +14 -4
  20. package/src/contentful/blocks/footer/types.ts +4 -0
  21. package/src/contentful/blocks/navigation/desktop-link-groups.tsx/index.test.tsx +26 -2
  22. package/src/contentful/blocks/navigation/desktop-link-groups.tsx/index.tsx +8 -2
  23. package/src/contentful/blocks/navigation/index.test.tsx +40 -2
  24. package/src/contentful/blocks/navigation/index.tsx +25 -5
  25. package/src/contentful/blocks/navigation/mobile-link-groups.tsx/index.test.tsx +14 -0
  26. package/src/contentful/blocks/navigation/mobile-link-groups.tsx/index.tsx +4 -1
  27. package/src/contentful/blocks/navigation/types.ts +5 -0
  28. 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 data-testid="next-image" src={src} alt={alt} onClick={onClick} />
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={callNowCtaText}
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 } = props;
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;