@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.
- package/__mocks__/next-image.tsx +3 -1
- package/package.json +3 -3
- package/src/api-manager/datasources/d365/d365-product.datasource.ts +1 -1
- package/src/api-manager/datasources/d365/utils/product-inventory-utils.ts +1 -3
- package/src/api-manager/datasources/e4/cart/e4-cart.remaps.ts +7 -5
- package/src/api-manager/datasources/e4/product/e4-product.datasource.ts +1 -4
- package/src/api-manager/datasources/e4/product/e4-product.translator.ts +13 -19
- package/src/api-manager/datasources/e4/utils/unwrap-e4-variants.ts +10 -13
- package/src/auth/auth-options.ts +34 -1
- package/src/auth/providers/entra-external-id-provider.ts +50 -0
- package/src/auth/providers/index.ts +8 -1
- package/src/auth/refresh-token.ts +1 -1
- package/src/cms/blocks/interfaces.ts +2 -0
- package/src/components-v2/BazaarMenu.stories.tsx +1 -1
- package/src/components-v2/BazaarMenu.tsx +4 -3
- package/src/components-v2/T/__tests__/T.test.tsx +91 -0
- package/src/components-v2/accordion/__tests__/accordion-header.test.tsx +28 -0
- package/src/components-v2/carousel/__tests__/carousel.test.tsx +208 -0
- package/src/components-v2/carousel-cards/carousel-card-1/__tests__/carousel-card-1.test.tsx +81 -0
- package/src/components-v2/categories/__tests__/category-menu.test.tsx +115 -0
- package/src/components-v2/categories/category-list/category-list.tsx +5 -1
- package/src/components-v2/categories/category-menu.tsx +11 -5
- package/src/components-v2/flex-box/__tests__/flex-box.test.tsx +88 -0
- package/src/components-v2/header/__tests__/header.test.tsx +122 -0
- package/src/components-v2/icons/__tests__/icons.test.tsx +53 -0
- package/src/components-v2/mobile-navigation/__tests__/mobile-navigation-bar.test.tsx +94 -0
- package/src/components-v2/nav-link/__tests__/nav-link.test.tsx +75 -0
- package/src/components-v2/navbar/__tests__/navbar.test.tsx +96 -0
- package/src/components-v2/product-cards/__tests__/product-cards.test.tsx +91 -0
- package/src/components-v2/product-dialog/__tests__/product-dialog.test.tsx +129 -0
- package/src/components-v2/product-dimensions/__tests__/product-dimensions.test.tsx +130 -0
- package/src/components-v2/product-dimensions/compound/dimensions-group-list-chip.tsx +36 -37
- package/src/components-v2/product-quantity-buttons/__tests__/product-quantity-buttons.test.tsx +77 -0
- package/src/components-v2/product-quantity-variants/__tests__/product-quantity-variants.test.tsx +109 -0
- package/src/components-v2/products-view/__tests__/products-view.test.tsx +147 -0
- package/src/components-v2/progress/__tests__/progress.test.tsx +62 -0
- package/src/components-v2/scrollbar/Scrollbar.stories.tsx +2 -2
- package/src/components-v2/scrollbar/__tests__/scrollbar.test.tsx +93 -0
- package/src/components-v2/search-bar/__tests__/search-bar.test.tsx +142 -0
- package/src/components-v2/search-bar/compound/results.tsx +1 -1
- package/src/components-v2/section/__tests__/section.test.tsx +100 -0
- package/src/components-v2/side-nav/__tests__/side-nav.test.tsx +156 -0
- package/src/components-v2/wishlist-dialogs/add-to-wishlist/add-to-wishlist-button.tsx +14 -15
- package/src/components-v2/wishlist-dialogs/styles.ts +8 -10
- package/src/pages-v2/account/addresses/address-item.tsx +2 -1
- package/src/pages-v2/checkout/checkout-alt-form/checkout-form.tsx +6 -1
- package/src/pages-v2/checkout/checkout-alt-form/steps/address/new-address-form.tsx +9 -8
- package/src/pages-v2/product-details/bopis/find-in-store-modal.tsx +1 -1
- package/src/pages-v2/product-details/product-intro/compound/thumbnail-with-skeleton.tsx +1 -0
- package/src/pages-v2/product-list/product-list-context.tsx +123 -44
- package/src/pages-v2/product-list/range-filter.tsx +36 -25
- 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={
|
|
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 {
|
|
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 = (
|
|
36
|
-
e.stopPropagation();
|
|
36
|
+
const onClick = () => {
|
|
37
37
|
setOpen((open) => !open);
|
|
38
38
|
};
|
|
39
39
|
|
|
40
|
-
const handleDocumentClick = useCallback(() =>
|
|
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
|
+
});
|