@evenicanpm/storefront-core 1.0.0-alpha → 1.0.1

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 (196) hide show
  1. package/package.json +3 -2
  2. package/src/api-manager/api-manager.config.json +1 -1
  3. package/src/api-manager/datasources/d365/d365-address.datasource.ts +4 -4
  4. package/src/api-manager/datasources/d365/d365-cart.datasource.ts +100 -7
  5. package/src/api-manager/datasources/d365/d365-product.datasource.ts +39 -17
  6. package/src/api-manager/datasources/d365/d365-user.datasource.ts +220 -68
  7. package/src/api-manager/datasources/d365/utils/decode-jwt.ts +2 -2
  8. package/src/api-manager/datasources/e4/e4-cart.datasource.ts +6 -0
  9. package/src/api-manager/datasources/e4/e4-product.datasource.ts +22 -2
  10. package/src/api-manager/datasources/e4/e4.datasource.ts +1 -1
  11. package/src/api-manager/datasources/e4/e4.remaps.ts +24 -0
  12. package/src/api-manager/datasources/e4/e4.translator.ts +19 -1
  13. package/src/api-manager/datasources/e4/{e4-user.datasource.ts → user/e4-user.datasource.ts} +41 -19
  14. package/src/api-manager/datasources/e4/user/e4-user.remaps.ts +4 -0
  15. package/src/api-manager/datasources/e4/user/e4-user.translator.ts +10 -0
  16. package/src/api-manager/index.ts +77 -15
  17. package/src/api-manager/lib/get-graphql-client.ts +7 -7
  18. package/src/api-manager/schemas/address.schema.ts +1 -19
  19. package/src/api-manager/schemas/cart.schema.ts +18 -0
  20. package/src/api-manager/schemas/product.schema.ts +8 -1
  21. package/src/api-manager/schemas/user.schema.ts +52 -1
  22. package/src/api-manager/services/address/queries/get-states.ts +1 -1
  23. package/src/api-manager/services/cart/mutations/checkout.ts +16 -1
  24. package/src/api-manager/services/cart/queries/get-cart-lines-inventory.ts +15 -0
  25. package/src/api-manager/services/create-extension-query.ts +26 -0
  26. package/src/api-manager/services/create-mutation.ts +5 -1
  27. package/src/api-manager/services/create-query.ts +10 -4
  28. package/src/api-manager/services/organization/queries/get-channel-configuration.ts +1 -1
  29. package/src/api-manager/services/product/queries/get-product-prices.ts +16 -0
  30. package/src/api-manager/services/product/queries/search-products.ts +1 -0
  31. package/src/api-manager/services/user/mutations/add-to-wishlist.ts +24 -0
  32. package/src/api-manager/services/user/mutations/copy-cart-to-wishlist.ts +23 -0
  33. package/src/api-manager/services/user/mutations/create-wishlist.ts +4 -4
  34. package/src/api-manager/services/user/mutations/delete-wishlist.ts +4 -3
  35. package/src/api-manager/services/user/mutations/remove-from-wishlist.ts +21 -0
  36. package/src/api-manager/services/user/mutations/update-wishlist.ts +23 -0
  37. package/src/api-manager/services/user/queries/get-wishlist-details.ts +11 -4
  38. package/src/api-manager/services/user/queries/get-wishlists.ts +4 -3
  39. package/src/api-manager/types/Datasource.ts +42 -14
  40. package/src/auth/auth-options.ts +2 -2
  41. package/src/auth/msal.ts +9 -9
  42. package/src/auth/next-auth-cookie-manager.ts +5 -3
  43. package/src/auth/providers/aadb2c-provider.ts +3 -4
  44. package/src/cms/endpoints.ts +2 -3
  45. package/src/components/Blocks/Components/ProductCarousel/index.tsx +6 -6
  46. package/src/components/_components/LazyImage.tsx +5 -2
  47. package/src/components/_components/header/components/user.tsx +1 -2
  48. package/src/components/_components/navbar/category-based-menu/components/categories.tsx +1 -1
  49. package/src/components/_components/navbar/mega-menu/mega-menu.tsx +1 -1
  50. package/src/components/_components/navbar/nav-list/nav-item-child.tsx +2 -2
  51. package/src/components/_components/product-cards/product-card-1/components/quantity-buttons.tsx +17 -8
  52. package/src/components/_components/product-cards/product-card-1/product-card-1-wrapper.tsx +304 -0
  53. package/src/components/_components/product-cards/product-card-1/product-card.tsx +16 -55
  54. package/src/components/_components/product-cards/product-card-8/product-card.tsx +80 -60
  55. package/src/components/_components/product-cards/product-card-8/styles/index.ts +10 -0
  56. package/src/components/_components/product-cards/product-card-wishlist/components/hover-actions.tsx +37 -0
  57. package/src/components/_components/product-cards/product-card-wishlist/index.ts +1 -0
  58. package/src/components/_components/product-cards/product-card-wishlist/product-card.tsx +118 -0
  59. package/src/components/_components/product-cards/product-card-wishlist/styles/index.ts +51 -0
  60. package/src/components/_components/product-dimensions/product-dimensions.tsx +114 -0
  61. package/src/components/_components/product-quantity-buttons/product-quantity-buttons.tsx +88 -0
  62. package/src/components/_components/products-view/product-view-dialog.tsx +48 -10
  63. package/src/components/_components/products-view/products-grid-view.tsx +31 -17
  64. package/src/components/_components/products-view/products-list-view.tsx +30 -16
  65. package/src/components/_components/search-box/__test__/search-input-category.test.tsx +110 -0
  66. package/src/components/_components/search-box/__test__/search-input.test.tsx +137 -0
  67. package/src/components/_components/search-box/__test__/search-results.json +165 -0
  68. package/src/components/_components/search-box/components/search-result.tsx +66 -12
  69. package/src/components/_components/search-box/search-input-category.tsx +135 -32
  70. package/src/components/_components/search-box/styles/index.ts +2 -0
  71. package/src/components/_components/section-header/section-creator.tsx +1 -0
  72. package/src/components/_components/section-header/section-header.tsx +1 -1
  73. package/src/components/_components/settings/setting.tsx +1 -1
  74. package/src/components/_components/wishlist/add-to-wishlist-dialog.tsx +161 -0
  75. package/src/components/_components/wishlist/components/create-new-list.tsx +167 -0
  76. package/src/components/_components/wishlist/components/create-wishlist-button.tsx +40 -0
  77. package/src/components/_components/wishlist/components/dashboard-header.tsx +123 -0
  78. package/src/components/_components/wishlist/components/navigation.tsx +87 -0
  79. package/src/components/_components/wishlist/components/no-records.tsx +20 -0
  80. package/src/components/_components/wishlist/components/wishlist-modal-item.tsx +66 -0
  81. package/src/components/_components/wishlist/remove-from-wishlist-dialog.tsx +174 -0
  82. package/src/components/_components/wishlist/styles.ts +61 -0
  83. package/src/components/_components/wishlist-modal/TODO.md +10 -0
  84. package/src/components/_components/wishlist-modal/add-to-wishlist-button.tsx +68 -0
  85. package/src/components/_components/wishlist-modal/add-to-wishlist-dialog.tsx +161 -0
  86. package/src/components/_components/wishlist-modal/remove-from-wishlist-dialog.tsx +174 -0
  87. package/src/components/_components/wishlist-modal/wishlist-modal-item.tsx +65 -0
  88. package/src/components/countries-input.tsx +7 -12
  89. package/src/hooks/use-cart.tsx +82 -0
  90. package/src/hooks/use-variants.tsx +85 -0
  91. package/src/lib/cart-cookie-handler-constants.ts +8 -0
  92. package/src/lib/cart-cookie-handler.ts +27 -5
  93. package/src/lib/create-graphql-client.ts +1 -2
  94. package/src/lib/product-list-types.ts +41 -0
  95. package/src/pages/account/addresses/address-form.tsx +289 -0
  96. package/src/pages/account/addresses/address-item.tsx +104 -0
  97. package/src/pages/account/dashboard-header.tsx +123 -0
  98. package/src/pages/account/navigation.tsx +84 -0
  99. package/src/pages/account/no-records.tsx +20 -0
  100. package/src/pages/account/orders/icons/delivery.tsx +14 -0
  101. package/src/pages/account/orders/icons/package-box.tsx +13 -0
  102. package/src/pages/account/orders/icons/truck-filled.tsx +14 -0
  103. package/src/pages/account/orders/order-progress.tsx +111 -0
  104. package/src/pages/account/orders/order-row.tsx +61 -0
  105. package/src/pages/account/orders/order-summary.tsx +83 -0
  106. package/src/pages/account/orders/ordered-products.tsx +114 -0
  107. package/src/pages/account/profile/profile-button.test.tsx +59 -0
  108. package/src/pages/account/profile/profile-button.tsx +50 -0
  109. package/src/pages/account/profile/user-info.tsx +59 -0
  110. package/src/pages/account/styles.ts +32 -0
  111. package/src/pages/account/table-row.tsx +19 -0
  112. package/src/pages/account/wishlist/create-new-list.tsx +167 -0
  113. package/src/pages/account/wishlist/create-wishlist-button.tsx +40 -0
  114. package/src/pages/account/wishlist/wishlist-item.tsx +82 -0
  115. package/src/pages/blog/pagination.tsx +38 -0
  116. package/src/pages/cart/cart-item.tsx +312 -0
  117. package/src/pages/cart/checkout-form.tsx +122 -0
  118. package/src/pages/cart/coupon-entry.tsx +90 -0
  119. package/src/pages/cart/estimate-shipping.tsx +183 -0
  120. package/src/pages/cart/wrapper.tsx +30 -0
  121. package/src/pages/checkout/checkout-alt-form/address-card.tsx +73 -0
  122. package/src/pages/checkout/checkout-alt-form/checkout-form.tsx +133 -0
  123. package/src/pages/checkout/checkout-alt-form/checkout-step.tsx +120 -0
  124. package/src/pages/checkout/checkout-alt-form/customer-information.tsx +94 -0
  125. package/src/pages/checkout/checkout-alt-form/delivery-address.tsx +170 -0
  126. package/src/pages/checkout/checkout-alt-form/delivery-date.tsx +114 -0
  127. package/src/pages/checkout/checkout-alt-form/delivery-options.tsx +186 -0
  128. package/src/pages/checkout/checkout-alt-form/edit-address-form.tsx +130 -0
  129. package/src/pages/checkout/checkout-alt-form/heading.tsx +50 -0
  130. package/src/pages/checkout/checkout-alt-form/index.ts +1 -0
  131. package/src/pages/checkout/checkout-alt-form/new-address-form.tsx +294 -0
  132. package/src/pages/checkout/checkout-alt-form/node.tsx +9 -0
  133. package/src/pages/checkout/checkout-alt-form/payment-details.tsx +344 -0
  134. package/src/pages/checkout/checkout-alt-form/safe-card-preview-data.tsx +36 -0
  135. package/src/pages/checkout/checkout-alt-form/types.ts +20 -0
  136. package/src/pages/checkout/checkout-alt-summary/cart-item.tsx +39 -0
  137. package/src/pages/checkout/checkout-alt-summary/checkout-alt-summary.tsx +40 -0
  138. package/src/pages/checkout/checkout-alt-summary/index.ts +1 -0
  139. package/src/pages/checkout/checkout-alt-summary/list-item.tsx +31 -0
  140. package/src/pages/confirmation/address.tsx +22 -0
  141. package/src/pages/confirmation/confirmation-summary.tsx +52 -0
  142. package/src/pages/confirmation/ordered-products.tsx +108 -0
  143. package/src/pages/product-details/available-shops.tsx +48 -0
  144. package/src/pages/product-details/bopis/find-in-store-button.tsx +63 -0
  145. package/src/pages/product-details/bopis/find-in-store-modal.tsx +257 -0
  146. package/src/pages/product-details/bopis/pickup-option-select.tsx +127 -0
  147. package/src/pages/product-details/bopis/search-header.tsx +69 -0
  148. package/src/pages/product-details/checkbox-label.tsx +20 -0
  149. package/src/pages/product-details/currency.ts +64 -0
  150. package/src/pages/product-details/frequently-bought.tsx +90 -0
  151. package/src/pages/product-details/frequently-product-card.tsx +60 -0
  152. package/src/pages/product-details/product-comment.tsx +44 -0
  153. package/src/pages/product-details/product-description.tsx +22 -0
  154. package/src/pages/product-details/product-filter-card.tsx +257 -0
  155. package/src/pages/product-details/product-intro/product-intro-images.tsx +87 -0
  156. package/src/pages/product-details/product-intro/product-intro.tsx +250 -0
  157. package/src/pages/product-details/product-review.tsx +131 -0
  158. package/src/pages/product-details/product-tabs.tsx +51 -0
  159. package/src/pages/product-details/related-products.tsx +42 -0
  160. package/src/pages/product-details/types.ts +11 -0
  161. package/src/pages/product-list/breadcrumbs.tsx +39 -0
  162. package/src/pages/product-list/checkbox-label.tsx +20 -0
  163. package/src/pages/product-list/facet-group.tsx +125 -0
  164. package/src/pages/product-list/facet.tsx +36 -0
  165. package/src/pages/product-list/list-filter.tsx +40 -0
  166. package/src/pages/product-list/pagination.tsx +80 -0
  167. package/src/pages/product-list/product-list-context.tsx +302 -0
  168. package/src/pages/product-list/product-list-state.ts +187 -0
  169. package/src/pages/product-list/product-rating.tsx +16 -0
  170. package/src/pages/product-list/quick-view-dialog-content.tsx +205 -0
  171. package/src/pages/product-list/quick-view-dialog.tsx +54 -0
  172. package/src/pages/product-list/range-filter.tsx +125 -0
  173. package/src/pages/product-list/scrollbar.tsx +38 -0
  174. package/src/pages/product-list/search-bar.tsx +162 -0
  175. package/src/pages/product-list/selected-facets.tsx +80 -0
  176. package/src/pages/product-list/side-nav.tsx +49 -0
  177. package/src/pages/product-list/swatch.tsx +80 -0
  178. package/src/pages/product-list/types.ts +18 -0
  179. package/src/pages/product-list/use-product-list.ts +12 -0
  180. package/src/pages/product-list/useCategoryFilter.tsx +26 -0
  181. package/src/pages/product-list/utils/facet-helpers.ts +5 -0
  182. package/src/pages/product-list/utils/generate-breadcrumbs.ts +30 -0
  183. package/src/pages/product-list/utils/getCategoryFilterFromSlug.ts +153 -0
  184. package/src/pages/product-list/utils/product-list-helper.ts +111 -0
  185. package/src/pages/product-list/utils/product-list-types.ts +41 -0
  186. package/src/pages/product-list/utils/search-for-category.ts +76 -0
  187. package/src/pages/product-list/utils/sort-options.ts +44 -0
  188. package/src/pages/product-list/utils/use-previous-refiners.ts +14 -0
  189. package/src/pages/quickorder/order-upload.tsx +150 -0
  190. package/src/pages/quickorder/quick-order-form.tsx +343 -0
  191. package/src/pages/quickorder/quick-order-row.tsx +144 -0
  192. package/tsconfig.json +1 -0
  193. package/src/api-manager/datasources/e4/graphqlRequestSdk.ts +0 -43515
  194. package/src/config.ts +0 -47
  195. /package/src/{components/_components/hooks/useOverflowDetect.ts → hooks/use-overflow-detect.ts} +0 -0
  196. /package/src/{components/_components/hooks/useSettings.ts → hooks/use-settings.ts} +0 -0
@@ -0,0 +1,110 @@
1
+ import SearchInputWithCategories from "../../../../../../../../../storefront/src/app/_components/search-box/search-input-category";
2
+ import { render, screen, waitFor } from "@testing-library/react";
3
+ import { expect, describe, it, vi, beforeAll } from "vitest";
4
+ import userEvent from "@testing-library/user-event";
5
+ import { categories } from "@app/_components/navbar/utils/transform-nav.test";
6
+ import NavProvider from "@components/NavProvider";
7
+ import { CategoryHierarchy } from "@lib/category-helpers";
8
+
9
+ beforeAll(() => {
10
+ vi.mock("next/navigation", () => ({
11
+ usePathname: vi.fn().mockReturnValue("/"),
12
+ }));
13
+ vi.mock("@services/product", () => {
14
+ return {
15
+ searchProducts: vi.fn().mockReturnValue([
16
+ {
17
+ Name: "Shorts",
18
+ ItemCode: 1,
19
+ Category: { RecordId: 10, ParentCategory: 11 },
20
+ },
21
+ {
22
+ Name: "Ladies Shorts",
23
+ ItemCode: 2,
24
+ Category: { RecordId: 2, ParentCategory: 1 },
25
+ },
26
+ ]),
27
+ };
28
+ });
29
+ vi.mock("next/headers", () => ({
30
+ cookies: vi.fn().mockReturnValue(new Map([["storefront", "token"]])),
31
+ }));
32
+ });
33
+
34
+ describe("SearchInputWithCategories", async () => {
35
+ it.skip("should render the search input and the category button", () => {
36
+ render(<SearchInputWithCategories />);
37
+ const input = screen.getByRole("textbox");
38
+ const categories = screen.getByText(/all categories/i);
39
+
40
+ expect(input).toBeInTheDocument();
41
+ expect(categories).toBeInTheDocument();
42
+ });
43
+
44
+ it.skip("Handles category change and changes the button text to selected.", async () => {
45
+ const user = userEvent.setup();
46
+
47
+ render(
48
+ <NavProvider
49
+ categories={categories as unknown as CategoryHierarchy[]}
50
+ cmsNav={[]}
51
+ >
52
+ <SearchInputWithCategories />
53
+ </NavProvider>,
54
+ );
55
+
56
+ const categoriesButton = screen.getByText(/all categories/i);
57
+ await user.click(categoriesButton);
58
+ const dropdownMenuItem = await screen.findByText("Women");
59
+
60
+ screen.debug(undefined, 10000);
61
+ await user.click(dropdownMenuItem);
62
+ const buttonWithNewText = screen.getByRole("button", {
63
+ name: /Women/i,
64
+ });
65
+
66
+ expect(buttonWithNewText).toBeInTheDocument();
67
+ });
68
+
69
+ it.skip("should render search results after user types", async () => {
70
+ const user = userEvent.setup();
71
+
72
+ render(<SearchInputWithCategories />);
73
+
74
+ const input = screen.getByRole("textbox");
75
+ expect(screen.queryByText("Shorts")).not.toBeInTheDocument();
76
+
77
+ await user.click(input);
78
+ await user.keyboard("Shorts"); // search term doesn't matter here, mock will return data regardless
79
+
80
+ expect(await screen.findByText("Shorts")).toBeInTheDocument();
81
+ });
82
+
83
+ it.skip("should filter search results by category", async () => {
84
+ const user = userEvent.setup();
85
+ render(
86
+ <NavProvider
87
+ categories={categories as unknown as CategoryHierarchy[]}
88
+ cmsNav={[]}
89
+ >
90
+ <SearchInputWithCategories />
91
+ </NavProvider>,
92
+ );
93
+
94
+ const categoriesButton = screen.getByText(/all categories/i);
95
+ const input = screen.getByRole("textbox");
96
+
97
+ await user.click(input);
98
+ await user.keyboard("Shorts");
99
+ expect(await screen.findByText("Shorts")).toBeInTheDocument();
100
+
101
+ await user.click(categoriesButton);
102
+ const dropdownMenuItem = await screen.findByText("Women");
103
+ await user.click(dropdownMenuItem);
104
+ await user.click(input);
105
+ await user.keyboard("Shorts");
106
+
107
+ waitFor(() => expect(screen.getByText("Shorts")).not.toBeInTheDocument());
108
+ expect(screen.queryByText("Ladies Shorts")).toBeInTheDocument();
109
+ });
110
+ });
@@ -0,0 +1,137 @@
1
+ import { render, screen } from "@testing-library/react";
2
+ import { expect, describe, it, vi, beforeAll } from "vitest";
3
+ import userEvent from "@testing-library/user-event";
4
+ import { NextIntlClientProvider } from "next-intl";
5
+ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
6
+ import ENUS from "../../../../../../../../../storefront/messages/en-US.json";
7
+ import { act } from "react";
8
+ import searchResultsData from "./search-results.json";
9
+ import SearchInput from "../search-input";
10
+ import { NotificationProvider } from "@/components/Notifications/notification-context";
11
+
12
+ const queryClient = new QueryClient();
13
+ const Wrapper = ({ children }: { children: React.ReactNode }) => (
14
+ <NextIntlClientProvider locale="en-US" messages={ENUS}>
15
+ <NotificationProvider>
16
+ <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
17
+ </NotificationProvider>
18
+ </NextIntlClientProvider>
19
+ );
20
+
21
+ const searchResults = searchResultsData;
22
+ const mockSearchProducts = vi.fn().mockResolvedValue(searchResults);
23
+
24
+ function sleep(ms: number) {
25
+ return new Promise((resolve) => setTimeout(resolve, ms));
26
+ }
27
+
28
+ beforeAll(() => {
29
+ vi.mock("next/navigation", () => ({
30
+ usePathname: vi.fn().mockReturnValue("/"),
31
+ }));
32
+ // vi.mock("@/api-manager/services/product/mutations/search-products", () => ({
33
+ // default: () => ({
34
+ // mutateAsync: vi.fn().mockReturnValue([
35
+ // {
36
+ // Name: "Amber Sports Sunglasses",
37
+ // ItemId: "91007",
38
+ // Price: 250,
39
+ // },
40
+ // ]),
41
+ // }),
42
+ // }));
43
+ vi.mock("@/api-manager/services/product/mutations/search-products", () => ({
44
+ default: () => ({
45
+ mutateAsync: mockSearchProducts,
46
+ }),
47
+ }));
48
+ vi.mock("next/headers", () => ({
49
+ cookies: vi.fn().mockReturnValue(new Map([["storefront", "token"]])),
50
+ }));
51
+ window.matchMedia = vi.fn().mockImplementation((query) => ({
52
+ matches: true, // to mock mobile screen
53
+ media: query,
54
+ onchange: null,
55
+ addListener: vi.fn(),
56
+ removeListener: vi.fn(),
57
+ addEventListener: vi.fn(),
58
+ removeEventListener: vi.fn(),
59
+ dispatchEvent: vi.fn(),
60
+ }));
61
+ // vi.mock("@mui/material/useMediaQuery", () => ({
62
+ // default: vi.fn().mockReturnValue(true), // to mock mobile screen
63
+ // }));
64
+ });
65
+ const handleCloseDrawer = vi.fn();
66
+ describe("SearchInput", () => {
67
+ it("should render the search input", () => {
68
+ render(
69
+ <Wrapper>
70
+ <SearchInput handleCloseDrawer={handleCloseDrawer} />
71
+ </Wrapper>,
72
+ );
73
+ const input = screen.getByRole("textbox");
74
+ expect(input).toBeInTheDocument();
75
+ });
76
+
77
+ it("should render search results with item names and prices after user types", async () => {
78
+ const user = userEvent.setup();
79
+
80
+ render(
81
+ <Wrapper>
82
+ <SearchInput handleCloseDrawer={handleCloseDrawer} />
83
+ </Wrapper>,
84
+ );
85
+ const input = screen.getAllByRole("textbox")[0];
86
+ expect(
87
+ screen.queryByText("Three Stone Diamond White Gold Ring"),
88
+ ).not.toBeInTheDocument();
89
+
90
+ // user.click(input);
91
+ // user.keyboard("shorts"); // search term doesn't matter here, mock will return data regardless
92
+
93
+ await act(async () => {
94
+ await user.type(input, "ring");
95
+ });
96
+ expect(input).toHaveValue("ring");
97
+ await act(async () => await sleep(1000)); // TODO: replace with debounce
98
+ expect(mockSearchProducts).toHaveBeenCalledTimes(1);
99
+ // make sure its mobile version of search results
100
+ expect(
101
+ await screen.findByText("Three Stone Diamond White Gold Ring"),
102
+ ).toBeInTheDocument();
103
+ expect(await screen.findByText("$410.00")).toBeInTheDocument();
104
+ });
105
+
106
+ it("should have the correct link", async () => {
107
+ const user = userEvent.setup();
108
+
109
+ render(
110
+ <Wrapper>
111
+ <SearchInput handleCloseDrawer={handleCloseDrawer} />
112
+ </Wrapper>,
113
+ );
114
+ const input = screen.getAllByRole("textbox")[0];
115
+ expect(
116
+ screen.queryByText("Three Stone Diamond White Gold Ring"),
117
+ ).not.toBeInTheDocument();
118
+
119
+ // user.click(input);
120
+ // user.keyboard("shorts"); // search term doesn't matter here, mock will return data regardless
121
+
122
+ await act(async () => {
123
+ await user.type(input, "ring");
124
+ });
125
+ expect(input).toHaveValue("ring");
126
+ await act(async () => await sleep(1000)); // TODO: replace with debounce
127
+ // make sure its mobile version of search results
128
+ const link = screen.getByRole("link", {
129
+ name: "product Three Stone Diamond White Gold Ring $410.00",
130
+ });
131
+ expect(link).toBeInTheDocument();
132
+ expect(link).toHaveAttribute(
133
+ "href",
134
+ "/product/Three Stone Diamond White Gold Ring/68719492176",
135
+ );
136
+ });
137
+ });
@@ -0,0 +1,165 @@
1
+ [
2
+ {
3
+ "ItemId": "91078",
4
+ "Name": "Three Stone Diamond White Gold Ring",
5
+ "Price": 410,
6
+ "PrimaryImageUrl": "Products/91078_000_001.png",
7
+ "RecordId": 68719492176,
8
+ "ProductNumber": "91078",
9
+ "AverageRating": 4.071428571428571,
10
+ "TotalRatings": 14,
11
+ "Description": "Three diamonds on a white gold band.",
12
+ "BasePrice": 410,
13
+ "AttributeValues": [],
14
+ "IsMasterProduct": true,
15
+ "MasterProductId": 68719492176,
16
+ "DefaultUnitOfMeasure": "ea",
17
+ "ExtensionProperties": []
18
+ },
19
+ {
20
+ "ItemId": "91079",
21
+ "Name": "Three Stone Diamond Trellis Ring",
22
+ "Price": 440,
23
+ "PrimaryImageUrl": "Products/91079_000_001.png",
24
+ "RecordId": 68719492185,
25
+ "ProductNumber": "91079",
26
+ "TotalRatings": 0,
27
+ "Description": "A trellis band complete with three stone diamonds.",
28
+ "BasePrice": 440,
29
+ "AttributeValues": [],
30
+ "IsMasterProduct": true,
31
+ "MasterProductId": 68719492185,
32
+ "DefaultUnitOfMeasure": "ea",
33
+ "ExtensionProperties": []
34
+ },
35
+ {
36
+ "ItemId": "91077",
37
+ "Name": "Four Stone Diamond White Gold Ring",
38
+ "Price": 360,
39
+ "PrimaryImageUrl": "Products/91077_000_001.png",
40
+ "RecordId": 68719492166,
41
+ "ProductNumber": "91077",
42
+ "TotalRatings": 0,
43
+ "Description": "A white gold band complete with four stone diamonds.",
44
+ "BasePrice": 360,
45
+ "AttributeValues": [],
46
+ "IsMasterProduct": true,
47
+ "MasterProductId": 68719492166,
48
+ "DefaultUnitOfMeasure": "ea",
49
+ "ExtensionProperties": []
50
+ },
51
+ {
52
+ "ItemId": "93040",
53
+ "Name": "Ombre Fringe Skirt",
54
+ "Price": 95,
55
+ "PrimaryImageUrl": "Products/93040_000_001.png",
56
+ "RecordId": 68719494571,
57
+ "ProductNumber": "93040",
58
+ "AverageRating": 4.235294117647059,
59
+ "TotalRatings": 17,
60
+ "Description": "This ombre red-to-white fringe skirt is both elegant and fun.",
61
+ "BasePrice": 95,
62
+ "AttributeValues": [],
63
+ "IsMasterProduct": true,
64
+ "MasterProductId": 68719494571,
65
+ "DefaultUnitOfMeasure": "ea",
66
+ "ExtensionProperties": []
67
+ },
68
+ {
69
+ "ItemId": "91085",
70
+ "Name": "Gold Wrapped Pearl Earrings",
71
+ "Price": 95,
72
+ "PrimaryImageUrl": "Products/91085_000_001.png",
73
+ "RecordId": 68719492200,
74
+ "ProductNumber": "91085",
75
+ "AverageRating": 3.9166666666666665,
76
+ "TotalRatings": 12,
77
+ "Description": "Pearls wrapped in gold held by golden clasps.",
78
+ "BasePrice": 95,
79
+ "AttributeValues": [],
80
+ "IsMasterProduct": false,
81
+ "MasterProductId": 0,
82
+ "DefaultUnitOfMeasure": "ea",
83
+ "ExtensionProperties": []
84
+ },
85
+ {
86
+ "ItemId": "91081",
87
+ "Name": "3\" Feather Earrings",
88
+ "Price": 55,
89
+ "PrimaryImageUrl": "Products/91081_000_001.png",
90
+ "RecordId": 68719492196,
91
+ "ProductNumber": "91081",
92
+ "TotalRatings": 0,
93
+ "Description": "Black 3\" feathers held by gold clips.",
94
+ "BasePrice": 55,
95
+ "AttributeValues": [],
96
+ "IsMasterProduct": false,
97
+ "MasterProductId": 0,
98
+ "DefaultUnitOfMeasure": "ea",
99
+ "ExtensionProperties": []
100
+ },
101
+ {
102
+ "ItemId": "91080",
103
+ "Name": "4\" Feather Earrings",
104
+ "Price": 60,
105
+ "PrimaryImageUrl": "Products/91080_000_001.png",
106
+ "RecordId": 68719492195,
107
+ "ProductNumber": "91080",
108
+ "TotalRatings": 0,
109
+ "Description": "Dark grey 4\" feathers at the end of gold clips.",
110
+ "BasePrice": 60,
111
+ "AttributeValues": [],
112
+ "IsMasterProduct": false,
113
+ "MasterProductId": 0,
114
+ "DefaultUnitOfMeasure": "ea",
115
+ "ExtensionProperties": []
116
+ },
117
+ {
118
+ "ItemId": "91045",
119
+ "Name": "Herringbone Fedora",
120
+ "Price": 180,
121
+ "PrimaryImageUrl": "Products/91045_000_001.png",
122
+ "RecordId": 68719495883,
123
+ "ProductNumber": "91045",
124
+ "TotalRatings": 0,
125
+ "Description": "A grey band circling a fedora complete with a herringbone pattern. \n",
126
+ "BasePrice": 180,
127
+ "AttributeValues": [],
128
+ "IsMasterProduct": true,
129
+ "MasterProductId": 68719495883,
130
+ "DefaultUnitOfMeasure": "ea",
131
+ "ExtensionProperties": []
132
+ },
133
+ {
134
+ "ItemId": "91086",
135
+ "Name": "Embedded Pearl Earrings",
136
+ "Price": 85,
137
+ "PrimaryImageUrl": "Products/91086_000_001.png",
138
+ "RecordId": 68719492201,
139
+ "ProductNumber": "91086",
140
+ "TotalRatings": 0,
141
+ "Description": "Pearls embedded in sterling silver earrings.",
142
+ "BasePrice": 85,
143
+ "AttributeValues": [],
144
+ "IsMasterProduct": false,
145
+ "MasterProductId": 0,
146
+ "DefaultUnitOfMeasure": "ea",
147
+ "ExtensionProperties": []
148
+ },
149
+ {
150
+ "ItemId": "91084",
151
+ "Name": "Golden Flower Earrings",
152
+ "Price": 120,
153
+ "PrimaryImageUrl": "Products/91084_000_001.png",
154
+ "RecordId": 68719492199,
155
+ "ProductNumber": "91084",
156
+ "TotalRatings": 0,
157
+ "Description": "Golden 1/4\" flowers held by golden clasps.",
158
+ "BasePrice": 120,
159
+ "AttributeValues": [],
160
+ "IsMasterProduct": false,
161
+ "MasterProductId": 0,
162
+ "DefaultUnitOfMeasure": "ea",
163
+ "ExtensionProperties": []
164
+ }
165
+ ]
@@ -4,26 +4,80 @@ import MenuItem from "@mui/material/MenuItem";
4
4
  import { SearchResultCard } from "../styles/index";
5
5
  import { ProductSearchResult } from "@msdyn365-commerce/retail-proxy";
6
6
  import { getProductDetailUrl } from "@/lib/build-product-detail-url";
7
+ import { Box, Grid, useMediaQuery, useTheme } from "@mui/material";
8
+ import LazyImage from "@/_components/LazyImage";
9
+ import { H4, Span } from "@/_components/Typography";
10
+ import { currency } from "@/lib/lib";
11
+ import ProductCard8 from "@/_components/product-cards/product-card-8/product-card";
12
+ import { DialogType } from "../search-input-category";
7
13
 
8
14
  interface Props {
9
15
  results: ProductSearchResult[];
10
16
  handleCloseDrawer?: () => void; // Close search drawer in mobile screen
17
+ openDialog: (type: DialogType, productId: number) => void;
11
18
  }
12
19
 
13
- export default function SearchResult({ results, handleCloseDrawer }: Props) {
20
+ export default function SearchResult({
21
+ results,
22
+ handleCloseDrawer,
23
+ openDialog,
24
+ }: Props) {
25
+ const theme = useTheme();
26
+ const downMd = useMediaQuery(theme.breakpoints.down(1150));
14
27
  return (
15
28
  <SearchResultCard>
16
- {results.map((item, i) => (
17
- <MenuItem key={item?.Name || "" + i}>
18
- <NavLink
19
- href={getProductDetailUrl(item)}
20
- key={item.RecordId}
21
- onClick={handleCloseDrawer}
22
- >
23
- {item.Name}
24
- </NavLink>
25
- </MenuItem>
26
- ))}
29
+ {downMd ? (
30
+ <>
31
+ {results.map((item, i) => (
32
+ <MenuItem key={item?.Name || "" + i} onClick={handleCloseDrawer}>
33
+ <NavLink href={getProductDetailUrl(item)} key={item.RecordId}>
34
+ <Box display="flex" alignItems="center">
35
+ <Box position="relative" height={130} width={130}>
36
+ <LazyImage
37
+ height={300}
38
+ width={300}
39
+ alt="product"
40
+ className="product-img"
41
+ src={item.PrimaryImageUrl}
42
+ />
43
+ </Box>
44
+ <Box flex="1" ml={2} whiteSpace="normal">
45
+ <H4>{item.Name}</H4>
46
+ <Span>{currency(item.Price)}</Span>
47
+ </Box>
48
+ </Box>
49
+ </NavLink>
50
+ </MenuItem>
51
+ ))}
52
+ </>
53
+ ) : (
54
+ <Grid container>
55
+ {results.map((item) => (
56
+ <Grid
57
+ key={item.ItemId}
58
+ item
59
+ xs={12}
60
+ sm={6}
61
+ md={4}
62
+ onClick={handleCloseDrawer}
63
+ >
64
+ <ProductCard8
65
+ slug={item.ItemId ?? ""}
66
+ price={item.Price}
67
+ title={item.Name ?? ""}
68
+ thumbnail={item.PrimaryImageUrl}
69
+ id={item.RecordId ?? ""}
70
+ rating={item.AverageRating ?? 0}
71
+ reviews={item.TotalRatings ?? 0}
72
+ categories={[]} // TODO: Category info not available in item
73
+ openDialog={openDialog}
74
+ enableWishlistDialog={false}
75
+ enableQuickViewDialog={false} // Enable quick view dialog
76
+ />
77
+ </Grid>
78
+ ))}
79
+ </Grid>
80
+ )}
27
81
  </SearchResultCard>
28
82
  );
29
83
  }