@evenicanpm/storefront-core 1.6.0 → 1.8.0

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 (52) hide show
  1. package/__mocks__/next-image.tsx +3 -1
  2. package/package.json +3 -3
  3. package/src/api-manager/datasources/d365/d365-product.datasource.ts +1 -1
  4. package/src/api-manager/datasources/d365/utils/product-inventory-utils.ts +1 -3
  5. package/src/api-manager/datasources/e4/cart/e4-cart.remaps.ts +7 -5
  6. package/src/api-manager/datasources/e4/product/e4-product.datasource.ts +1 -4
  7. package/src/api-manager/datasources/e4/product/e4-product.translator.ts +13 -19
  8. package/src/api-manager/datasources/e4/utils/unwrap-e4-variants.ts +10 -13
  9. package/src/auth/auth-options.ts +34 -1
  10. package/src/auth/providers/entra-external-id-provider.ts +50 -0
  11. package/src/auth/providers/index.ts +8 -1
  12. package/src/auth/refresh-token.ts +1 -1
  13. package/src/cms/blocks/interfaces.ts +2 -0
  14. package/src/components-v2/BazaarMenu.stories.tsx +1 -1
  15. package/src/components-v2/BazaarMenu.tsx +4 -3
  16. package/src/components-v2/T/__tests__/T.test.tsx +91 -0
  17. package/src/components-v2/accordion/__tests__/accordion-header.test.tsx +28 -0
  18. package/src/components-v2/carousel/__tests__/carousel.test.tsx +208 -0
  19. package/src/components-v2/carousel-cards/carousel-card-1/__tests__/carousel-card-1.test.tsx +81 -0
  20. package/src/components-v2/categories/__tests__/category-menu.test.tsx +115 -0
  21. package/src/components-v2/categories/category-list/category-list.tsx +5 -1
  22. package/src/components-v2/categories/category-menu.tsx +11 -5
  23. package/src/components-v2/flex-box/__tests__/flex-box.test.tsx +88 -0
  24. package/src/components-v2/header/__tests__/header.test.tsx +122 -0
  25. package/src/components-v2/icons/__tests__/icons.test.tsx +53 -0
  26. package/src/components-v2/mobile-navigation/__tests__/mobile-navigation-bar.test.tsx +94 -0
  27. package/src/components-v2/nav-link/__tests__/nav-link.test.tsx +75 -0
  28. package/src/components-v2/navbar/__tests__/navbar.test.tsx +96 -0
  29. package/src/components-v2/product-cards/__tests__/product-cards.test.tsx +91 -0
  30. package/src/components-v2/product-dialog/__tests__/product-dialog.test.tsx +129 -0
  31. package/src/components-v2/product-dimensions/__tests__/product-dimensions.test.tsx +130 -0
  32. package/src/components-v2/product-dimensions/compound/dimensions-group-list-chip.tsx +36 -37
  33. package/src/components-v2/product-quantity-buttons/__tests__/product-quantity-buttons.test.tsx +77 -0
  34. package/src/components-v2/product-quantity-variants/__tests__/product-quantity-variants.test.tsx +109 -0
  35. package/src/components-v2/products-view/__tests__/products-view.test.tsx +147 -0
  36. package/src/components-v2/progress/__tests__/progress.test.tsx +62 -0
  37. package/src/components-v2/scrollbar/Scrollbar.stories.tsx +2 -2
  38. package/src/components-v2/scrollbar/__tests__/scrollbar.test.tsx +93 -0
  39. package/src/components-v2/search-bar/__tests__/search-bar.test.tsx +142 -0
  40. package/src/components-v2/search-bar/compound/results.tsx +1 -1
  41. package/src/components-v2/section/__tests__/section.test.tsx +100 -0
  42. package/src/components-v2/side-nav/__tests__/side-nav.test.tsx +156 -0
  43. package/src/components-v2/wishlist-dialogs/add-to-wishlist/add-to-wishlist-button.tsx +14 -15
  44. package/src/components-v2/wishlist-dialogs/styles.ts +8 -10
  45. package/src/pages-v2/account/addresses/address-item.tsx +2 -1
  46. package/src/pages-v2/checkout/checkout-alt-form/checkout-form.tsx +6 -1
  47. package/src/pages-v2/checkout/checkout-alt-form/steps/address/new-address-form.tsx +9 -8
  48. package/src/pages-v2/product-details/bopis/find-in-store-modal.tsx +1 -1
  49. package/src/pages-v2/product-details/product-intro/compound/thumbnail-with-skeleton.tsx +1 -0
  50. package/src/pages-v2/product-list/product-list-context.tsx +123 -44
  51. package/src/pages-v2/product-list/range-filter.tsx +36 -25
  52. package/src/pages-v2/product-list/selected-facets.tsx +15 -8
@@ -0,0 +1,208 @@
1
+ import { createTheme, ThemeProvider } from "@mui/material/styles";
2
+ import { render, screen } from "@testing-library/react";
3
+ import type React from "react";
4
+ import { vi } from "vitest";
5
+ import Carousel from "../carousel";
6
+
7
+ vi.mock("react-slick", () => {
8
+ const Slider = ({
9
+ children,
10
+ rtl,
11
+ autoplay,
12
+ autoplaySpeed,
13
+ slidesToShow,
14
+ arrows,
15
+ dots,
16
+ nextArrow,
17
+ prevArrow,
18
+ appendDots,
19
+ customPaging,
20
+ }: {
21
+ children: React.ReactNode;
22
+ rtl?: boolean;
23
+ autoplay?: boolean;
24
+ autoplaySpeed?: number;
25
+ slidesToShow?: number;
26
+ arrows?: boolean;
27
+ dots?: boolean;
28
+ nextArrow?: React.ReactNode;
29
+ prevArrow?: React.ReactNode;
30
+ appendDots?: (dots: React.ReactNode) => React.ReactNode;
31
+ customPaging?: (index: number) => React.ReactNode;
32
+ }) => (
33
+ <div
34
+ data-testid="mock-slider"
35
+ data-rtl={String(Boolean(rtl))}
36
+ data-autoplay={String(Boolean(autoplay))}
37
+ data-autoplay-speed={String(autoplaySpeed ?? "")}
38
+ data-slides-to-show={String(slidesToShow ?? "")}
39
+ data-arrows={String(Boolean(arrows))}
40
+ data-dots={String(Boolean(dots))}
41
+ >
42
+ <div data-testid="mock-arrows">
43
+ {nextArrow}
44
+ {prevArrow}
45
+ </div>
46
+ {dots && appendDots && customPaging ? (
47
+ <div data-testid="mock-dots">
48
+ {appendDots(<li>{customPaging(0)}</li>)}
49
+ </div>
50
+ ) : null}
51
+ <div data-testid="mock-children">{children}</div>
52
+ </div>
53
+ );
54
+
55
+ return {
56
+ default: Slider,
57
+ };
58
+ });
59
+
60
+ const renderWithTheme = (
61
+ ui: React.ReactElement,
62
+ direction: "ltr" | "rtl" = "ltr",
63
+ ) => {
64
+ const theme = createTheme({ direction });
65
+ return render(<ThemeProvider theme={theme}>{ui}</ThemeProvider>);
66
+ };
67
+
68
+ describe("carousel", () => {
69
+ it("renders slide content", () => {
70
+ renderWithTheme(
71
+ <Carousel>
72
+ <div>Slide One</div>
73
+ <div>Slide Two</div>
74
+ </Carousel>,
75
+ );
76
+
77
+ expect(screen.getByText("Slide One")).toBeInTheDocument();
78
+ expect(screen.getByText("Slide Two")).toBeInTheDocument();
79
+ });
80
+
81
+ it("enables custom arrows when arrows config is provided", () => {
82
+ renderWithTheme(
83
+ <Carousel arrows={{ sx: { color: "red" } }}>
84
+ <div>Slide</div>
85
+ </Carousel>,
86
+ );
87
+
88
+ const slider = screen.getByTestId("mock-slider");
89
+ expect(slider).toHaveAttribute("data-arrows", "true");
90
+ expect(document.querySelector(".next")).toBeInTheDocument();
91
+ expect(document.querySelector(".prev")).toBeInTheDocument();
92
+ });
93
+
94
+ it("enables custom dots when dots config is provided", () => {
95
+ renderWithTheme(
96
+ <Carousel dots={{ dotColor: "orange" }}>
97
+ <div>Slide</div>
98
+ </Carousel>,
99
+ );
100
+
101
+ const slider = screen.getByTestId("mock-slider");
102
+ expect(slider).toHaveAttribute("data-dots", "true");
103
+ expect(screen.getByTestId("mock-dots")).toBeInTheDocument();
104
+ });
105
+
106
+ it("enables custom arrows when legacy arrowStyles is provided", () => {
107
+ renderWithTheme(
108
+ <Carousel arrowStyles={{ color: "blue" }}>
109
+ <div>Slide</div>
110
+ </Carousel>,
111
+ );
112
+
113
+ const slider = screen.getByTestId("mock-slider");
114
+ expect(slider).toHaveAttribute("data-arrows", "true");
115
+ expect(document.querySelector(".next")).toBeInTheDocument();
116
+ expect(document.querySelector(".prev")).toBeInTheDocument();
117
+ });
118
+
119
+ it("uses react-slick default arrows when arrows is true", () => {
120
+ renderWithTheme(
121
+ <Carousel arrows={true}>
122
+ <div>Slide</div>
123
+ </Carousel>,
124
+ );
125
+
126
+ const slider = screen.getByTestId("mock-slider");
127
+ expect(slider).toHaveAttribute("data-arrows", "true");
128
+ expect(document.querySelector(".next")).not.toBeInTheDocument();
129
+ expect(document.querySelector(".prev")).not.toBeInTheDocument();
130
+ });
131
+
132
+ it("disables arrows by default", () => {
133
+ renderWithTheme(
134
+ <Carousel>
135
+ <div>Slide</div>
136
+ </Carousel>,
137
+ );
138
+
139
+ expect(screen.getByTestId("mock-slider")).toHaveAttribute(
140
+ "data-arrows",
141
+ "false",
142
+ );
143
+ });
144
+
145
+ it("enables custom dots when legacy dotColor or dotStyles is provided", () => {
146
+ renderWithTheme(
147
+ <Carousel dotColor="green" dotStyles={{ bottom: 0 }}>
148
+ <div>Slide</div>
149
+ </Carousel>,
150
+ );
151
+
152
+ const slider = screen.getByTestId("mock-slider");
153
+ expect(slider).toHaveAttribute("data-dots", "true");
154
+ expect(screen.getByTestId("mock-dots")).toBeInTheDocument();
155
+ });
156
+
157
+ it("uses react-slick default dots when dots is true", () => {
158
+ renderWithTheme(
159
+ <Carousel dots={true}>
160
+ <div>Slide</div>
161
+ </Carousel>,
162
+ );
163
+
164
+ const slider = screen.getByTestId("mock-slider");
165
+ expect(slider).toHaveAttribute("data-dots", "true");
166
+ expect(screen.queryByTestId("mock-dots")).not.toBeInTheDocument();
167
+ });
168
+
169
+ it("disables dots by default", () => {
170
+ renderWithTheme(
171
+ <Carousel>
172
+ <div>Slide</div>
173
+ </Carousel>,
174
+ );
175
+
176
+ expect(screen.getByTestId("mock-slider")).toHaveAttribute(
177
+ "data-dots",
178
+ "false",
179
+ );
180
+ });
181
+
182
+ it("passes rtl setting from theme direction", () => {
183
+ renderWithTheme(
184
+ <Carousel>
185
+ <div>RTL Slide</div>
186
+ </Carousel>,
187
+ "rtl",
188
+ );
189
+
190
+ expect(screen.getByTestId("mock-slider")).toHaveAttribute(
191
+ "data-rtl",
192
+ "true",
193
+ );
194
+ });
195
+
196
+ it("passes autoplay and other settings through to slider", () => {
197
+ renderWithTheme(
198
+ <Carousel autoplay={true} autoplaySpeed={4500} slidesToShow={3}>
199
+ <div>Slide</div>
200
+ </Carousel>,
201
+ );
202
+
203
+ const slider = screen.getByTestId("mock-slider");
204
+ expect(slider).toHaveAttribute("data-autoplay", "true");
205
+ expect(slider).toHaveAttribute("data-autoplay-speed", "4500");
206
+ expect(slider).toHaveAttribute("data-slides-to-show", "3");
207
+ });
208
+ });
@@ -0,0 +1,81 @@
1
+ import { render, screen } from "@testing-library/react";
2
+ import CarouselCard1 from "../carousel-card-1";
3
+
4
+ describe("carousel-card-1", () => {
5
+ it("renders composed carousel card content", () => {
6
+ render(
7
+ <CarouselCard1>
8
+ <CarouselCard1.Info>
9
+ <CarouselCard1.Title title="Amazing Product" />
10
+ <CarouselCard1.Description description="Built for everyday comfort" />
11
+ <CarouselCard1.Button
12
+ buttonLink="/products/amazing"
13
+ buttonText="Buy Now"
14
+ />
15
+ </CarouselCard1.Info>
16
+ <CarouselCard1.Image imgUrl="/images/product.png" />
17
+ </CarouselCard1>,
18
+ );
19
+
20
+ expect(
21
+ screen.getByRole("heading", { name: "Amazing Product" }),
22
+ ).toBeInTheDocument();
23
+ expect(screen.getByText("Built for everyday comfort")).toBeInTheDocument();
24
+ expect(screen.getByRole("button", { name: "Buy Now" })).toBeInTheDocument();
25
+ expect(
26
+ screen.getByRole("img", { name: "carousel card image" }),
27
+ ).toBeInTheDocument();
28
+ });
29
+
30
+ it("renders info section children", () => {
31
+ render(
32
+ <CarouselCard1.Info>
33
+ <div>Info content</div>
34
+ </CarouselCard1.Info>,
35
+ );
36
+
37
+ expect(screen.getByText("Info content")).toBeInTheDocument();
38
+ });
39
+
40
+ it("renders title text", () => {
41
+ render(<CarouselCard1.Title title="Featured Item" />);
42
+
43
+ expect(
44
+ screen.getByRole("heading", { name: "Featured Item" }),
45
+ ).toBeInTheDocument();
46
+ });
47
+
48
+ it("renders description text", () => {
49
+ render(<CarouselCard1.Description description="Limited time offer" />);
50
+
51
+ expect(screen.getByText("Limited time offer")).toBeInTheDocument();
52
+ });
53
+
54
+ it("renders button with link and text", () => {
55
+ render(<CarouselCard1.Button buttonLink="/sale" buttonText="Shop Sale" />);
56
+
57
+ expect(
58
+ screen.getByRole("button", { name: "Shop Sale" }),
59
+ ).toBeInTheDocument();
60
+ expect(screen.getByRole("link", { name: "Shop Sale" })).toHaveAttribute(
61
+ "href",
62
+ "/sale",
63
+ );
64
+ });
65
+
66
+ it("renders image with provided source", () => {
67
+ render(<CarouselCard1.Image imgUrl="/images/carousel-item.png" />);
68
+
69
+ expect(
70
+ screen.getByRole("img", { name: "carousel card image" }),
71
+ ).toHaveAttribute("src", "/images/carousel-item.png");
72
+ });
73
+
74
+ it("renders image with empty source when imgUrl is not provided", () => {
75
+ render(<CarouselCard1.Image />);
76
+
77
+ expect(
78
+ screen.getByRole("img", { name: "carousel card image" }),
79
+ ).not.toHaveAttribute("src");
80
+ });
81
+ });
@@ -0,0 +1,115 @@
1
+ import { fireEvent, render, screen } from "@testing-library/react";
2
+ import { vi } from "vitest";
3
+ import CategoryMenu from "../category-menu";
4
+
5
+ vi.mock(
6
+ "@evenicanpm/storefront-core/src/components-v2/categories/category-list/index",
7
+ () => ({
8
+ default: ({ open }: { open: boolean }) => (
9
+ <div data-testid="category-list" data-open={String(open)}>
10
+ Category List
11
+ </div>
12
+ ),
13
+ }),
14
+ );
15
+
16
+ describe("category-menu", () => {
17
+ it("renders with the provided render prop", () => {
18
+ render(
19
+ <CategoryMenu
20
+ render={(handler) => (
21
+ <button type="button" onClick={handler} data-testid="toggle-btn">
22
+ Toggle Categories
23
+ </button>
24
+ )}
25
+ />,
26
+ );
27
+
28
+ expect(screen.getByTestId("toggle-btn")).toBeInTheDocument();
29
+ expect(screen.getByTestId("category-list")).toBeInTheDocument();
30
+ });
31
+
32
+ it("toggles dropdown open state on click", () => {
33
+ render(
34
+ <CategoryMenu
35
+ render={(handler) => (
36
+ <button type="button" onClick={handler} data-testid="toggle-btn">
37
+ Toggle
38
+ </button>
39
+ )}
40
+ />,
41
+ );
42
+
43
+ const button = screen.getByTestId("toggle-btn");
44
+ const categoryList = screen.getByTestId("category-list");
45
+
46
+ expect(categoryList).toHaveAttribute("data-open", "false");
47
+
48
+ fireEvent.click(button);
49
+ expect(categoryList).toHaveAttribute("data-open", "true");
50
+
51
+ fireEvent.click(button);
52
+ expect(categoryList).toHaveAttribute("data-open", "false");
53
+ });
54
+
55
+ it("closes dropdown when clicking outside", () => {
56
+ render(
57
+ <div>
58
+ <CategoryMenu
59
+ render={(handler) => (
60
+ <button type="button" onClick={handler} data-testid="toggle-btn">
61
+ Toggle
62
+ </button>
63
+ )}
64
+ />
65
+ <div data-testid="outside">Outside Element</div>
66
+ </div>,
67
+ );
68
+
69
+ const button = screen.getByTestId("toggle-btn");
70
+ const categoryList = screen.getByTestId("category-list");
71
+ const outside = screen.getByTestId("outside");
72
+
73
+ fireEvent.click(button);
74
+ expect(categoryList).toHaveAttribute("data-open", "true");
75
+
76
+ fireEvent.click(outside);
77
+ expect(categoryList).toHaveAttribute("data-open", "false");
78
+ });
79
+
80
+ it("does not close dropdown when clicking inside it", () => {
81
+ render(
82
+ <CategoryMenu
83
+ render={(handler) => (
84
+ <button type="button" onClick={handler} data-testid="toggle-btn">
85
+ Toggle
86
+ </button>
87
+ )}
88
+ />,
89
+ );
90
+
91
+ const button = screen.getByTestId("toggle-btn");
92
+ const categoryList = screen.getByTestId("category-list");
93
+
94
+ fireEvent.click(button);
95
+ expect(categoryList).toHaveAttribute("data-open", "true");
96
+
97
+ fireEvent.click(categoryList);
98
+ expect(categoryList).toHaveAttribute("data-open", "true");
99
+ });
100
+
101
+ it("applies dropdown-icon styling", () => {
102
+ const { container } = render(
103
+ <CategoryMenu
104
+ render={(handler) => (
105
+ <button type="button" onClick={handler}>
106
+ <span className="dropdown-icon">Icon</span>
107
+ </button>
108
+ )}
109
+ />,
110
+ );
111
+
112
+ const icon = container.querySelector(".dropdown-icon");
113
+ expect(icon).toBeInTheDocument();
114
+ });
115
+ });
@@ -82,7 +82,11 @@ CategoryList.Item = ({ category }: ItemProps) => {
82
82
  href={`/products/${Url}`}
83
83
  title={Name ?? ""}
84
84
  caret={hasChildren}
85
- render={hasChildren ? megaMenuRenderers[menuType]?.(Children!) : null}
85
+ render={
86
+ Children && Children.length > 0
87
+ ? megaMenuRenderers[menuType]?.(Children)
88
+ : null
89
+ }
86
90
  />
87
91
  );
88
92
  };
@@ -2,7 +2,7 @@
2
2
 
3
3
  import CategoryList from "@evenicanpm/storefront-core/src/components-v2/categories/category-list/index";
4
4
  import styled from "@mui/material/styles/styled";
5
- import { type MouseEvent, useCallback, useEffect, useState } from "react";
5
+ import { useCallback, useEffect, useRef, useState } from "react";
6
6
 
7
7
  // STYLED COMPONENT
8
8
  const Wrapper = styled("div", {
@@ -31,13 +31,19 @@ type Props = {
31
31
  // not as a reusable UI element that needs further extensibility.
32
32
  export default function CategoryMenu({ render }: Readonly<Props>) {
33
33
  const [open, setOpen] = useState(false);
34
+ const wrapperRef = useRef<HTMLDivElement | null>(null);
34
35
 
35
- const onClick = (e: MouseEvent<HTMLButtonElement>) => {
36
- e.stopPropagation();
36
+ const onClick = () => {
37
37
  setOpen((open) => !open);
38
38
  };
39
39
 
40
- const handleDocumentClick = useCallback(() => setOpen(false), []);
40
+ const handleDocumentClick = useCallback((e: globalThis.MouseEvent) => {
41
+ const target = e.target as Node | null;
42
+ if (target && wrapperRef.current?.contains(target)) {
43
+ return;
44
+ }
45
+ setOpen(false);
46
+ }, []);
41
47
 
42
48
  useEffect(() => {
43
49
  document.addEventListener("click", handleDocumentClick);
@@ -45,7 +51,7 @@ export default function CategoryMenu({ render }: Readonly<Props>) {
45
51
  }, [handleDocumentClick]);
46
52
 
47
53
  return (
48
- <Wrapper open={open}>
54
+ <Wrapper ref={wrapperRef} open={open}>
49
55
  {render(onClick)}
50
56
 
51
57
  <CategoryList open={open} />
@@ -0,0 +1,88 @@
1
+ import { render, screen } from "@testing-library/react";
2
+ import FlexBox from "../flex-box";
3
+ import FlexBetween from "../flex-between";
4
+ import FlexRowCenter from "../flex-row-center";
5
+
6
+ describe("FlexBox components", () => {
7
+ describe("FlexBox", () => {
8
+ it("renders children and applies flex display", () => {
9
+ render(
10
+ <FlexBox data-testid="flex-box-test">
11
+ <div>Child 1</div>
12
+ <div>Child 2</div>
13
+ </FlexBox>,
14
+ );
15
+
16
+ expect(screen.getByText("Child 1")).toBeInTheDocument();
17
+ expect(screen.getByText("Child 2")).toBeInTheDocument();
18
+ expect(screen.getByTestId("flex-box-test")).toBeInTheDocument();
19
+ });
20
+
21
+ it("accepts and applies custom BoxProps", () => {
22
+ render(
23
+ <FlexBox data-testid="flex-box" gap={2} padding={1}>
24
+ Content
25
+ </FlexBox>,
26
+ );
27
+
28
+ const box = screen.getByTestId("flex-box");
29
+ expect(box).toBeInTheDocument();
30
+ expect(box).toHaveStyle("display: flex");
31
+ });
32
+ });
33
+
34
+ describe("FlexBetween", () => {
35
+ it("renders children with space-between and center alignment", () => {
36
+ render(
37
+ <FlexBetween data-testid="flex-between">
38
+ <div>Left</div>
39
+ <div>Right</div>
40
+ </FlexBetween>,
41
+ );
42
+
43
+ expect(screen.getByText("Left")).toBeInTheDocument();
44
+ expect(screen.getByText("Right")).toBeInTheDocument();
45
+
46
+ const box = screen.getByTestId("flex-between");
47
+ expect(box).toHaveStyle("display: flex");
48
+ expect(box).toHaveStyle("justify-content: space-between");
49
+ expect(box).toHaveStyle("align-items: center");
50
+ });
51
+
52
+ it("accepts custom props", () => {
53
+ render(
54
+ <FlexBetween data-testid="flex-between-custom" sx={{ height: 100 }}>
55
+ Content
56
+ </FlexBetween>,
57
+ );
58
+
59
+ expect(screen.getByTestId("flex-between-custom")).toBeInTheDocument();
60
+ });
61
+ });
62
+
63
+ describe("FlexRowCenter", () => {
64
+ it("renders children centered both horizontally and vertically", () => {
65
+ render(
66
+ <FlexRowCenter data-testid="flex-row-center">
67
+ <span>Centered</span>
68
+ </FlexRowCenter>,
69
+ );
70
+
71
+ const box = screen.getByTestId("flex-row-center");
72
+ expect(screen.getByText("Centered")).toBeInTheDocument();
73
+ expect(box).toHaveStyle("display: flex");
74
+ expect(box).toHaveStyle("justify-content: center");
75
+ expect(box).toHaveStyle("align-items: center");
76
+ });
77
+
78
+ it("applies custom styles", () => {
79
+ render(
80
+ <FlexRowCenter data-testid="flex-row-custom" minHeight={200}>
81
+ Flex Center
82
+ </FlexRowCenter>,
83
+ );
84
+
85
+ expect(screen.getByTestId("flex-row-custom")).toBeInTheDocument();
86
+ });
87
+ });
88
+ });
@@ -0,0 +1,122 @@
1
+ import { render, screen } from "@testing-library/react";
2
+ import type { ReactNode } from "react";
3
+ import { vi } from "vitest";
4
+ import Header from "../header";
5
+
6
+ vi.mock("@evenicanpm/storefront-core/src/hooks/use-nextauth-session", () => ({
7
+ useAppSession: vi.fn(() => ({ session: null })),
8
+ }));
9
+
10
+ vi.mock(
11
+ "@evenicanpm/storefront-core/src/api-manager/services/cart/queries/get-cart",
12
+ () => ({
13
+ default: {
14
+ useData: vi.fn(() => ({ data: null })),
15
+ },
16
+ }),
17
+ );
18
+
19
+ vi.mock(
20
+ "@evenicanpm/storefront-core/src/components-v2/mini-cart/mini-cart-trigger",
21
+ () => ({
22
+ default: ({ children }: { children: React.ReactNode }) => (
23
+ <div data-testid="mini-cart-trigger">{children}</div>
24
+ ),
25
+ }),
26
+ );
27
+
28
+ vi.mock("@evenicanpm/storefront-core/src/components-v2/site-logo", () => ({
29
+ default: () => <div data-testid="site-logo">Logo</div>,
30
+ }));
31
+
32
+ vi.mock(
33
+ "@evenicanpm/storefront-core/src/components-v2/header/components/categories-menu",
34
+ () => ({
35
+ default: () => <div data-testid="categories-menu">Categories</div>,
36
+ }),
37
+ );
38
+
39
+ vi.mock(
40
+ "@evenicanpm/storefront-core/src/components-v2/header/components/user",
41
+ () => {
42
+ const User = ({ children }: { children?: ReactNode }) => (
43
+ <div data-testid="user-component">User: {children}</div>
44
+ );
45
+ User.DesktopTrigger = () => <div data-testid="user-trigger">Trigger</div>;
46
+ User.Menu = () => <div data-testid="user-menu">Menu</div>;
47
+ User.LoginButton = () => (
48
+ <button type="button" data-testid="login-button">
49
+ Login
50
+ </button>
51
+ );
52
+ return { default: User };
53
+ },
54
+ );
55
+
56
+ describe("header", () => {
57
+ it("renders header wrapper with children", () => {
58
+ render(
59
+ <Header>
60
+ <div>Header Content</div>
61
+ </Header>,
62
+ );
63
+
64
+ expect(screen.getByText("Header Content")).toBeInTheDocument();
65
+ });
66
+
67
+ it("renders LeftSlot with children", () => {
68
+ render(
69
+ <Header.LeftSlot>
70
+ <span>Left Content</span>
71
+ </Header.LeftSlot>,
72
+ );
73
+
74
+ expect(screen.getByText("Left Content")).toBeInTheDocument();
75
+ });
76
+
77
+ it("renders Logo subcomponent", () => {
78
+ render(<Header.Logo />);
79
+
80
+ expect(screen.getByTestId("site-logo")).toBeInTheDocument();
81
+ expect(screen.getByText("Logo")).toBeInTheDocument();
82
+ });
83
+
84
+ it("renders Categories subcomponent", () => {
85
+ render(<Header.Categories />);
86
+
87
+ expect(screen.getByTestId("categories-menu")).toBeInTheDocument();
88
+ });
89
+
90
+ it("renders MidSlot with children", () => {
91
+ render(
92
+ <Header.MidSlot>
93
+ <span>Mid Content</span>
94
+ </Header.MidSlot>,
95
+ );
96
+
97
+ expect(screen.getByText("Mid Content")).toBeInTheDocument();
98
+ });
99
+
100
+ it("renders User subcomponent", () => {
101
+ render(<Header.User />);
102
+
103
+ expect(screen.getByTestId("user-component")).toBeInTheDocument();
104
+ });
105
+
106
+ it("renders Cart subcomponent", () => {
107
+ render(<Header.Cart />);
108
+
109
+ expect(screen.queryByTestId("mini-cart-trigger")).not.toBeInTheDocument();
110
+ });
111
+
112
+ it("accepts custom className", () => {
113
+ const { container } = render(
114
+ <Header className="custom-header">
115
+ <div>Content</div>
116
+ </Header>,
117
+ );
118
+
119
+ const wrapper = container.firstChild;
120
+ expect(wrapper).toHaveClass("custom-header");
121
+ });
122
+ });