@shopify/shop-minis-react 0.2.5 → 0.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_virtual/index4.js +2 -2
- package/dist/_virtual/index5.js +3 -2
- package/dist/_virtual/index5.js.map +1 -1
- package/dist/_virtual/index6.js +2 -3
- package/dist/_virtual/index6.js.map +1 -1
- package/dist/components/commerce/product-card.js +61 -58
- package/dist/components/commerce/product-card.js.map +1 -1
- package/dist/components/commerce/product-link.js +144 -131
- package/dist/components/commerce/product-link.js.map +1 -1
- package/dist/components/commerce/search.js +15 -15
- package/dist/components/commerce/search.js.map +1 -1
- package/dist/components/ui/drawer.js +30 -28
- package/dist/components/ui/drawer.js.map +1 -1
- package/dist/shop-minis-react/node_modules/.pnpm/@videojs_xhr@2.7.0/node_modules/@videojs/xhr/lib/index.js +1 -1
- package/dist/shop-minis-react/node_modules/.pnpm/mpd-parser@1.3.1/node_modules/mpd-parser/dist/mpd-parser.es.js +1 -1
- package/dist/shop-minis-react/node_modules/.pnpm/querystringify@2.2.0/node_modules/querystringify/index.js +1 -1
- package/dist/shop-minis-react/node_modules/.pnpm/simple-swizzle@0.2.2/node_modules/simple-swizzle/index.js +1 -1
- package/dist/shop-minis-react/node_modules/.pnpm/use-sync-external-store@1.5.0_react@19.1.0/node_modules/use-sync-external-store/shim/index.js +1 -1
- package/eslint/rules/validate-manifest.cjs +99 -1
- package/package.json +1 -1
- package/src/components/commerce/product-card.test.tsx +68 -0
- package/src/components/commerce/product-card.tsx +10 -2
- package/src/components/commerce/product-link.test.tsx +81 -0
- package/src/components/commerce/product-link.tsx +36 -10
- package/src/components/commerce/search.tsx +1 -1
- package/src/components/ui/drawer.tsx +7 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search.js","sources":["../../../src/components/commerce/search.tsx"],"sourcesContent":["import * as React from 'react'\nimport {createContext, useContext, useState, useCallback} from 'react'\n\nimport {SearchIcon, X} from 'lucide-react'\n\nimport {useProductSearch} from '../../hooks/product/useProductSearch'\nimport {cn} from '../../lib/utils'\nimport {type Product} from '../../types'\nimport {IconButton} from '../atoms/icon-button'\nimport {List} from '../atoms/list'\nimport {Input} from '../ui/input'\n\nimport {ProductLink} from './product-link'\nimport {ProductLinkSkeleton} from './product-link-skeleton'\n\ninterface SearchContextValue {\n query: string\n setQuery: (query: string) => void\n products: Product[] | null\n loading: boolean\n error: Error | null\n fetchMore?: () => Promise<void>\n hasNextPage: boolean\n isTyping: boolean\n}\n\nconst SearchContext = createContext<SearchContextValue | null>(null)\n\nfunction useSearchContext() {\n const context = useContext(SearchContext)\n if (!context) {\n throw new Error('useSearchContext must be used within a SearchProvider')\n }\n return context\n}\n\nexport interface SearchProviderProps {\n initialQuery?: string\n children: React.ReactNode\n}\n\nfunction SearchProvider({initialQuery = '', children}: SearchProviderProps) {\n const [query, setQueryState] = useState(initialQuery)\n\n const {products, loading, error, fetchMore, hasNextPage, isTyping} =\n useProductSearch({\n query,\n fetchPolicy: 'network-only',\n })\n\n const setQuery = useCallback((newQuery: string) => {\n setQueryState(newQuery)\n }, [])\n\n const contextValue: SearchContextValue = {\n query,\n setQuery,\n products,\n loading,\n error,\n fetchMore,\n hasNextPage,\n isTyping,\n }\n\n return (\n <SearchContext.Provider value={contextValue}>\n {children}\n </SearchContext.Provider>\n )\n}\n\nexport interface SearchInputProps {\n placeholder?: string\n className?: string\n inputProps?: React.ComponentProps<'input'>\n}\n\nfunction SearchInput({\n placeholder = 'Search products...',\n className,\n inputProps,\n}: SearchInputProps) {\n const {query, setQuery} = useSearchContext()\n\n const handleQueryChange = useCallback(\n (event: React.ChangeEvent<HTMLInputElement>) => {\n setQuery(event.target.value)\n inputProps?.onChange?.(event)\n },\n [inputProps, setQuery]\n )\n\n return (\n <div className=\"relative flex flex-1 items-center rounded-full pl-4 pr-2 py-1 bg-gray-100\">\n <div className=\"relative flex items-center\">\n <SearchIcon\n size={18}\n className={cn('text-accent-foreground opacity-60')}\n />\n </div>\n <div className=\"relative flex-1 flex items-center mx-2\">\n <Input\n name=\"search\"\n onChange={handleQueryChange}\n placeholder={placeholder}\n type=\"search\"\n role=\"searchbox\"\n autoComplete=\"off\"\n value={query}\n data-testid=\"search-input\"\n {...inputProps}\n className={cn(\n `w-full flex overflow-hidden rounded-radius-28 border-none py-4 px-0 text-text placeholder:text-text placeholder:opacity-60`,\n className\n )}\n />\n </div>\n <div className=\"relative flex items-center\">\n {query === '' ? null : (\n <IconButton\n Icon={X}\n size=\"sm\"\n filled={false}\n iconStyles=\"\"\n onClick={() => setQuery('')}\n buttonStyles=\"flex items-center rounded-radius-max bg-[var(--grayscale-l20)]\"\n />\n )}\n </div>\n </div>\n )\n}\n\nexport interface SearchResultsListProps {\n renderItem?: (product: Product, index: number) => React.ReactNode\n height?: number\n itemHeight?: number\n initialStateComponent?: React.JSX.Element\n showScrollbar?: boolean\n overscanCount?: number\n}\n\nfunction SearchResultsList({\n height = window.innerHeight,\n renderItem,\n initialStateComponent,\n showScrollbar,\n}: SearchResultsListProps) {\n const {query, products, loading, fetchMore, hasNextPage, isTyping} =\n useSearchContext()\n\n const _renderItem = (product: Product, index: number) => {\n if (renderItem) {\n return renderItem(product, index)\n }\n\n return (\n <div className=\"p-2\">\n <ProductLink key={product.id} product={product} hideFavoriteAction />\n </div>\n )\n }\n\n const shouldShowStartingState = query.trim().length === 0\n const shouldShowLoading =\n (!products || products.length === 0) && (loading || isTyping)\n const shouldShowEmptyState = (!products || products.length === 0) && !loading\n\n if (shouldShowStartingState) {\n return (\n initialStateComponent || (\n <div className=\"flex items-center justify-center h-32 text-gray-500\">\n Start typing to search for products\n </div>\n )\n )\n }\n\n if (shouldShowLoading) {\n return (\n <div className=\"flex flex-col px-4 py-4\">\n <ProductLinkSkeleton className=\"mb-4\" />\n <ProductLinkSkeleton className=\"mb-4\" />\n <ProductLinkSkeleton className=\"mb-4\" />\n <ProductLinkSkeleton className=\"mb-4\" />\n </div>\n )\n }\n\n if (shouldShowEmptyState) {\n return (\n <div className=\"flex items-center justify-center h-32 text-gray-500\">\n {`No products found for \"${query}\"`}\n </div>\n )\n }\n\n return (\n <List\n items={products || []}\n height={height}\n renderItem={_renderItem}\n fetchMore={hasNextPage ? fetchMore : undefined}\n showScrollbar={showScrollbar}\n />\n )\n}\n\ninterface SearchProviderPropsWithoutChildren\n extends Omit<SearchProviderProps, 'children'> {}\nexport interface SearchResultsProps\n extends SearchProviderPropsWithoutChildren,\n SearchInputProps,\n SearchResultsListProps {\n showSearchInput?: boolean\n onProductClick?: (product: Product) => void\n}\n\nfunction Search({\n initialQuery,\n placeholder,\n inputProps,\n height,\n className,\n renderItem,\n itemHeight,\n onProductClick,\n}: SearchResultsProps) {\n const _renderItem = (product: Product, index: number) => {\n if (renderItem) {\n return renderItem(product, index)\n }\n\n return (\n <div className=\"p-2\">\n <ProductLink\n key={product.id}\n product={product}\n hideFavoriteAction\n onClick={onProductClick}\n />\n </div>\n )\n }\n\n return (\n <SearchProvider initialQuery={initialQuery}>\n <div className={cn('flex flex-col ', className)}>\n <div className=\"fixed top-0 left-0 right-0 p-4 w-full z-20 bg-background\">\n <SearchInput placeholder={placeholder} inputProps={inputProps} />\n </div>\n <div className=\"h-14\" />\n <SearchResultsList\n height={height}\n renderItem={_renderItem}\n itemHeight={itemHeight}\n showScrollbar\n />\n </div>\n </SearchProvider>\n )\n}\n\nexport {SearchProvider, SearchInput, SearchResultsList, Search}\n"],"names":["SearchContext","createContext","useSearchContext","context","useContext","SearchProvider","initialQuery","children","query","setQueryState","useState","products","loading","error","fetchMore","hasNextPage","isTyping","useProductSearch","setQuery","useCallback","newQuery","contextValue","SearchInput","placeholder","className","inputProps","handleQueryChange","event","jsxs","jsx","SearchIcon","cn","Input","IconButton","X","SearchResultsList","height","renderItem","initialStateComponent","showScrollbar","_renderItem","product","index","ProductLink","shouldShowStartingState","shouldShowLoading","shouldShowEmptyState","ProductLinkSkeleton","List","Search","itemHeight","onProductClick"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"search.js","sources":["../../../src/components/commerce/search.tsx"],"sourcesContent":["import * as React from 'react'\nimport {createContext, useContext, useState, useCallback} from 'react'\n\nimport {SearchIcon, X} from 'lucide-react'\n\nimport {useProductSearch} from '../../hooks/product/useProductSearch'\nimport {cn} from '../../lib/utils'\nimport {type Product} from '../../types'\nimport {IconButton} from '../atoms/icon-button'\nimport {List} from '../atoms/list'\nimport {Input} from '../ui/input'\n\nimport {ProductLink} from './product-link'\nimport {ProductLinkSkeleton} from './product-link-skeleton'\n\ninterface SearchContextValue {\n query: string\n setQuery: (query: string) => void\n products: Product[] | null\n loading: boolean\n error: Error | null\n fetchMore?: () => Promise<void>\n hasNextPage: boolean\n isTyping: boolean\n}\n\nconst SearchContext = createContext<SearchContextValue | null>(null)\n\nfunction useSearchContext() {\n const context = useContext(SearchContext)\n if (!context) {\n throw new Error('useSearchContext must be used within a SearchProvider')\n }\n return context\n}\n\nexport interface SearchProviderProps {\n initialQuery?: string\n children: React.ReactNode\n}\n\nfunction SearchProvider({initialQuery = '', children}: SearchProviderProps) {\n const [query, setQueryState] = useState(initialQuery)\n\n const {products, loading, error, fetchMore, hasNextPage, isTyping} =\n useProductSearch({\n query,\n fetchPolicy: 'network-only',\n })\n\n const setQuery = useCallback((newQuery: string) => {\n setQueryState(newQuery)\n }, [])\n\n const contextValue: SearchContextValue = {\n query,\n setQuery,\n products,\n loading,\n error,\n fetchMore,\n hasNextPage,\n isTyping,\n }\n\n return (\n <SearchContext.Provider value={contextValue}>\n {children}\n </SearchContext.Provider>\n )\n}\n\nexport interface SearchInputProps {\n placeholder?: string\n className?: string\n inputProps?: React.ComponentProps<'input'>\n}\n\nfunction SearchInput({\n placeholder = 'Search products...',\n className,\n inputProps,\n}: SearchInputProps) {\n const {query, setQuery} = useSearchContext()\n\n const handleQueryChange = useCallback(\n (event: React.ChangeEvent<HTMLInputElement>) => {\n setQuery(event.target.value)\n inputProps?.onChange?.(event)\n },\n [inputProps, setQuery]\n )\n\n return (\n <div className=\"relative flex flex-1 items-center rounded-full pl-4 pr-2 py-1 bg-gray-100\">\n <div className=\"relative flex items-center\">\n <SearchIcon\n size={18}\n className={cn('text-accent-foreground opacity-60')}\n />\n </div>\n <div className=\"relative flex-1 flex items-center mx-2\">\n <Input\n name=\"search\"\n onChange={handleQueryChange}\n placeholder={placeholder}\n type=\"search\"\n role=\"searchbox\"\n autoComplete=\"off\"\n value={query}\n data-testid=\"search-input\"\n {...inputProps}\n className={cn(\n `w-full flex overflow-hidden rounded-radius-28 border-none shadow-none py-4 px-0 text-text placeholder:text-text placeholder:opacity-60`,\n className\n )}\n />\n </div>\n <div className=\"relative flex items-center\">\n {query === '' ? null : (\n <IconButton\n Icon={X}\n size=\"sm\"\n filled={false}\n iconStyles=\"\"\n onClick={() => setQuery('')}\n buttonStyles=\"flex items-center rounded-radius-max bg-[var(--grayscale-l20)]\"\n />\n )}\n </div>\n </div>\n )\n}\n\nexport interface SearchResultsListProps {\n renderItem?: (product: Product, index: number) => React.ReactNode\n height?: number\n itemHeight?: number\n initialStateComponent?: React.JSX.Element\n showScrollbar?: boolean\n overscanCount?: number\n}\n\nfunction SearchResultsList({\n height = window.innerHeight,\n renderItem,\n initialStateComponent,\n showScrollbar,\n}: SearchResultsListProps) {\n const {query, products, loading, fetchMore, hasNextPage, isTyping} =\n useSearchContext()\n\n const _renderItem = (product: Product, index: number) => {\n if (renderItem) {\n return renderItem(product, index)\n }\n\n return (\n <div className=\"p-2\">\n <ProductLink key={product.id} product={product} hideFavoriteAction />\n </div>\n )\n }\n\n const shouldShowStartingState = query.trim().length === 0\n const shouldShowLoading =\n (!products || products.length === 0) && (loading || isTyping)\n const shouldShowEmptyState = (!products || products.length === 0) && !loading\n\n if (shouldShowStartingState) {\n return (\n initialStateComponent || (\n <div className=\"flex items-center justify-center h-32 text-gray-500\">\n Start typing to search for products\n </div>\n )\n )\n }\n\n if (shouldShowLoading) {\n return (\n <div className=\"flex flex-col px-4 py-4\">\n <ProductLinkSkeleton className=\"mb-4\" />\n <ProductLinkSkeleton className=\"mb-4\" />\n <ProductLinkSkeleton className=\"mb-4\" />\n <ProductLinkSkeleton className=\"mb-4\" />\n </div>\n )\n }\n\n if (shouldShowEmptyState) {\n return (\n <div className=\"flex items-center justify-center h-32 text-gray-500\">\n {`No products found for \"${query}\"`}\n </div>\n )\n }\n\n return (\n <List\n items={products || []}\n height={height}\n renderItem={_renderItem}\n fetchMore={hasNextPage ? fetchMore : undefined}\n showScrollbar={showScrollbar}\n />\n )\n}\n\ninterface SearchProviderPropsWithoutChildren\n extends Omit<SearchProviderProps, 'children'> {}\nexport interface SearchResultsProps\n extends SearchProviderPropsWithoutChildren,\n SearchInputProps,\n SearchResultsListProps {\n showSearchInput?: boolean\n onProductClick?: (product: Product) => void\n}\n\nfunction Search({\n initialQuery,\n placeholder,\n inputProps,\n height,\n className,\n renderItem,\n itemHeight,\n onProductClick,\n}: SearchResultsProps) {\n const _renderItem = (product: Product, index: number) => {\n if (renderItem) {\n return renderItem(product, index)\n }\n\n return (\n <div className=\"p-2\">\n <ProductLink\n key={product.id}\n product={product}\n hideFavoriteAction\n onClick={onProductClick}\n />\n </div>\n )\n }\n\n return (\n <SearchProvider initialQuery={initialQuery}>\n <div className={cn('flex flex-col ', className)}>\n <div className=\"fixed top-0 left-0 right-0 p-4 w-full z-20 bg-background\">\n <SearchInput placeholder={placeholder} inputProps={inputProps} />\n </div>\n <div className=\"h-14\" />\n <SearchResultsList\n height={height}\n renderItem={_renderItem}\n itemHeight={itemHeight}\n showScrollbar\n />\n </div>\n </SearchProvider>\n )\n}\n\nexport {SearchProvider, SearchInput, SearchResultsList, Search}\n"],"names":["SearchContext","createContext","useSearchContext","context","useContext","SearchProvider","initialQuery","children","query","setQueryState","useState","products","loading","error","fetchMore","hasNextPage","isTyping","useProductSearch","setQuery","useCallback","newQuery","contextValue","SearchInput","placeholder","className","inputProps","handleQueryChange","event","jsxs","jsx","SearchIcon","cn","Input","IconButton","X","SearchResultsList","height","renderItem","initialStateComponent","showScrollbar","_renderItem","product","index","ProductLink","shouldShowStartingState","shouldShowLoading","shouldShowEmptyState","ProductLinkSkeleton","List","Search","itemHeight","onProductClick"],"mappings":"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"}
|
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
import { jsx as a, jsxs as o } from "react/jsx-runtime";
|
|
2
2
|
import { Drawer as e } from "../../shop-minis-react/node_modules/.pnpm/vaul@1.1.2_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react@19.1.6_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/vaul/dist/index.js";
|
|
3
3
|
import { cn as d } from "../../lib/utils.js";
|
|
4
|
-
function
|
|
4
|
+
function m({ ...t }) {
|
|
5
5
|
return /* @__PURE__ */ a(e.Root, { "data-slot": "drawer", ...t });
|
|
6
6
|
}
|
|
7
|
-
function
|
|
7
|
+
function f({
|
|
8
8
|
...t
|
|
9
9
|
}) {
|
|
10
10
|
return /* @__PURE__ */ a(e.Trigger, { "data-slot": "drawer-trigger", ...t });
|
|
11
11
|
}
|
|
12
|
-
function
|
|
12
|
+
function l({
|
|
13
13
|
...t
|
|
14
14
|
}) {
|
|
15
15
|
return /* @__PURE__ */ a(e.Portal, { "data-slot": "drawer-portal", ...t });
|
|
16
16
|
}
|
|
17
|
-
function
|
|
17
|
+
function v({
|
|
18
18
|
...t
|
|
19
19
|
}) {
|
|
20
20
|
return /* @__PURE__ */ a(e.Close, { "data-slot": "drawer-close", ...t });
|
|
21
21
|
}
|
|
22
|
-
function
|
|
22
|
+
function c({
|
|
23
23
|
className: t,
|
|
24
24
|
...r
|
|
25
25
|
}) {
|
|
@@ -35,38 +35,40 @@ function l({
|
|
|
35
35
|
}
|
|
36
36
|
);
|
|
37
37
|
}
|
|
38
|
-
function
|
|
38
|
+
function p({
|
|
39
39
|
className: t,
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
fullHeight: r = !1,
|
|
41
|
+
children: i,
|
|
42
|
+
...n
|
|
42
43
|
}) {
|
|
43
44
|
return (
|
|
44
45
|
// vaul's Portal type incorrectly excludes children
|
|
45
|
-
/* @__PURE__ */ o(
|
|
46
|
-
/* @__PURE__ */ a(
|
|
46
|
+
/* @__PURE__ */ o(l, { children: [
|
|
47
|
+
/* @__PURE__ */ a(c, {}),
|
|
47
48
|
/* @__PURE__ */ o(
|
|
48
49
|
e.Content,
|
|
49
50
|
{
|
|
50
51
|
"data-slot": "drawer-content",
|
|
51
52
|
className: d(
|
|
52
53
|
"group/drawer-content bg-background fixed z-50 flex h-auto flex-col",
|
|
53
|
-
"data-[vaul-drawer-direction=top]:inset-x-0 data-[vaul-drawer-direction=top]:top-0 data-[vaul-drawer-direction=top]:mb-24 data-[vaul-drawer-direction=top]:max-h-[
|
|
54
|
-
"data-[vaul-drawer-direction=bottom]:inset-x-0 data-[vaul-drawer-direction=bottom]:bottom-0 data-[vaul-drawer-direction=bottom]:mt-24 data-[vaul-drawer-direction=bottom]:max-h-[
|
|
54
|
+
"data-[vaul-drawer-direction=top]:inset-x-0 data-[vaul-drawer-direction=top]:top-0 data-[vaul-drawer-direction=top]:mb-24 data-[vaul-drawer-direction=top]:max-h-[100vh] data-[vaul-drawer-direction=top]:rounded-b-lg data-[vaul-drawer-direction=top]:border-b",
|
|
55
|
+
"data-[vaul-drawer-direction=bottom]:inset-x-0 data-[vaul-drawer-direction=bottom]:bottom-0 data-[vaul-drawer-direction=bottom]:mt-24 data-[vaul-drawer-direction=bottom]:max-h-[100vh] data-[vaul-drawer-direction=bottom]:rounded-t-[28px] data-[vaul-drawer-direction=bottom]:border-t",
|
|
55
56
|
"data-[vaul-drawer-direction=right]:inset-y-0 data-[vaul-drawer-direction=right]:right-0 data-[vaul-drawer-direction=right]:w-3/4 data-[vaul-drawer-direction=right]:border-l data-[vaul-drawer-direction=right]:sm:max-w-sm",
|
|
56
57
|
"data-[vaul-drawer-direction=left]:inset-y-0 data-[vaul-drawer-direction=left]:left-0 data-[vaul-drawer-direction=left]:w-3/4 data-[vaul-drawer-direction=left]:border-r data-[vaul-drawer-direction=left]:sm:max-w-sm",
|
|
58
|
+
r ? "h-[100vh]" : "",
|
|
57
59
|
t
|
|
58
60
|
),
|
|
59
|
-
...
|
|
61
|
+
...n,
|
|
60
62
|
children: [
|
|
61
63
|
/* @__PURE__ */ a("div", { className: "bg-muted mx-auto mt-4 hidden h-2 w-[100px] shrink-0 rounded-full group-data-[vaul-drawer-direction=bottom]/drawer-content:block" }),
|
|
62
|
-
|
|
64
|
+
i
|
|
63
65
|
]
|
|
64
66
|
}
|
|
65
67
|
)
|
|
66
68
|
] })
|
|
67
69
|
);
|
|
68
70
|
}
|
|
69
|
-
function
|
|
71
|
+
function g({ className: t, ...r }) {
|
|
70
72
|
return /* @__PURE__ */ a(
|
|
71
73
|
"div",
|
|
72
74
|
{
|
|
@@ -79,7 +81,7 @@ function p({ className: t, ...r }) {
|
|
|
79
81
|
}
|
|
80
82
|
);
|
|
81
83
|
}
|
|
82
|
-
function
|
|
84
|
+
function x({ className: t, ...r }) {
|
|
83
85
|
return /* @__PURE__ */ a(
|
|
84
86
|
"div",
|
|
85
87
|
{
|
|
@@ -89,7 +91,7 @@ function g({ className: t, ...r }) {
|
|
|
89
91
|
}
|
|
90
92
|
);
|
|
91
93
|
}
|
|
92
|
-
function
|
|
94
|
+
function b({
|
|
93
95
|
className: t,
|
|
94
96
|
...r
|
|
95
97
|
}) {
|
|
@@ -102,7 +104,7 @@ function x({
|
|
|
102
104
|
}
|
|
103
105
|
);
|
|
104
106
|
}
|
|
105
|
-
function
|
|
107
|
+
function h({
|
|
106
108
|
className: t,
|
|
107
109
|
...r
|
|
108
110
|
}) {
|
|
@@ -116,15 +118,15 @@ function b({
|
|
|
116
118
|
);
|
|
117
119
|
}
|
|
118
120
|
export {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
121
|
+
m as Drawer,
|
|
122
|
+
v as DrawerClose,
|
|
123
|
+
p as DrawerContent,
|
|
124
|
+
h as DrawerDescription,
|
|
125
|
+
x as DrawerFooter,
|
|
126
|
+
g as DrawerHeader,
|
|
127
|
+
c as DrawerOverlay,
|
|
128
|
+
l as DrawerPortal,
|
|
129
|
+
b as DrawerTitle,
|
|
130
|
+
f as DrawerTrigger
|
|
129
131
|
};
|
|
130
132
|
//# sourceMappingURL=drawer.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"drawer.js","sources":["../../../src/components/ui/drawer.tsx"],"sourcesContent":["import * as React from 'react'\n\nimport {Drawer as DrawerPrimitive} from 'vaul'\n\nimport {cn} from '../../lib/utils'\n\nfunction Drawer({...props}: React.ComponentProps<typeof DrawerPrimitive.Root>) {\n return <DrawerPrimitive.Root data-slot=\"drawer\" {...props} />\n}\n\nfunction DrawerTrigger({\n ...props\n}: React.ComponentProps<typeof DrawerPrimitive.Trigger>) {\n return <DrawerPrimitive.Trigger data-slot=\"drawer-trigger\" {...props} />\n}\n\nfunction DrawerPortal({\n ...props\n}: React.ComponentProps<typeof DrawerPrimitive.Portal>) {\n return <DrawerPrimitive.Portal data-slot=\"drawer-portal\" {...props} />\n}\n\nfunction DrawerClose({\n ...props\n}: React.ComponentProps<typeof DrawerPrimitive.Close>) {\n return <DrawerPrimitive.Close data-slot=\"drawer-close\" {...props} />\n}\n\nfunction DrawerOverlay({\n className,\n ...props\n}: React.ComponentProps<typeof DrawerPrimitive.Overlay>) {\n return (\n <DrawerPrimitive.Overlay\n data-slot=\"drawer-overlay\"\n className={cn(\n 'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50',\n className\n )}\n {...props}\n />\n )\n}\n\nfunction DrawerContent({\n className,\n children,\n ...props\n}: React.ComponentProps<typeof DrawerPrimitive.Content>) {\n return (\n // vaul's Portal type incorrectly excludes children\n <DrawerPortal {...({} as any)}>\n <DrawerOverlay />\n <DrawerPrimitive.Content\n data-slot=\"drawer-content\"\n className={cn(\n 'group/drawer-content bg-background fixed z-50 flex h-auto flex-col',\n 'data-[vaul-drawer-direction=top]:inset-x-0 data-[vaul-drawer-direction=top]:top-0 data-[vaul-drawer-direction=top]:mb-24 data-[vaul-drawer-direction=top]:max-h-[
|
|
1
|
+
{"version":3,"file":"drawer.js","sources":["../../../src/components/ui/drawer.tsx"],"sourcesContent":["import * as React from 'react'\n\nimport {Drawer as DrawerPrimitive} from 'vaul'\n\nimport {cn} from '../../lib/utils'\n\nfunction Drawer({...props}: React.ComponentProps<typeof DrawerPrimitive.Root>) {\n return <DrawerPrimitive.Root data-slot=\"drawer\" {...props} />\n}\n\nfunction DrawerTrigger({\n ...props\n}: React.ComponentProps<typeof DrawerPrimitive.Trigger>) {\n return <DrawerPrimitive.Trigger data-slot=\"drawer-trigger\" {...props} />\n}\n\nfunction DrawerPortal({\n ...props\n}: React.ComponentProps<typeof DrawerPrimitive.Portal>) {\n return <DrawerPrimitive.Portal data-slot=\"drawer-portal\" {...props} />\n}\n\nfunction DrawerClose({\n ...props\n}: React.ComponentProps<typeof DrawerPrimitive.Close>) {\n return <DrawerPrimitive.Close data-slot=\"drawer-close\" {...props} />\n}\n\nfunction DrawerOverlay({\n className,\n ...props\n}: React.ComponentProps<typeof DrawerPrimitive.Overlay>) {\n return (\n <DrawerPrimitive.Overlay\n data-slot=\"drawer-overlay\"\n className={cn(\n 'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50',\n className\n )}\n {...props}\n />\n )\n}\n\nfunction DrawerContent({\n className,\n fullHeight = false,\n children,\n ...props\n}: React.ComponentProps<typeof DrawerPrimitive.Content> & {\n fullHeight?: boolean\n}) {\n return (\n // vaul's Portal type incorrectly excludes children\n <DrawerPortal {...({} as any)}>\n <DrawerOverlay />\n <DrawerPrimitive.Content\n data-slot=\"drawer-content\"\n className={cn(\n 'group/drawer-content bg-background fixed z-50 flex h-auto flex-col',\n 'data-[vaul-drawer-direction=top]:inset-x-0 data-[vaul-drawer-direction=top]:top-0 data-[vaul-drawer-direction=top]:mb-24 data-[vaul-drawer-direction=top]:max-h-[100vh] data-[vaul-drawer-direction=top]:rounded-b-lg data-[vaul-drawer-direction=top]:border-b',\n 'data-[vaul-drawer-direction=bottom]:inset-x-0 data-[vaul-drawer-direction=bottom]:bottom-0 data-[vaul-drawer-direction=bottom]:mt-24 data-[vaul-drawer-direction=bottom]:max-h-[100vh] data-[vaul-drawer-direction=bottom]:rounded-t-[28px] data-[vaul-drawer-direction=bottom]:border-t',\n 'data-[vaul-drawer-direction=right]:inset-y-0 data-[vaul-drawer-direction=right]:right-0 data-[vaul-drawer-direction=right]:w-3/4 data-[vaul-drawer-direction=right]:border-l data-[vaul-drawer-direction=right]:sm:max-w-sm',\n 'data-[vaul-drawer-direction=left]:inset-y-0 data-[vaul-drawer-direction=left]:left-0 data-[vaul-drawer-direction=left]:w-3/4 data-[vaul-drawer-direction=left]:border-r data-[vaul-drawer-direction=left]:sm:max-w-sm',\n fullHeight ? 'h-[100vh]' : '',\n className\n )}\n {...props}\n >\n <div className=\"bg-muted mx-auto mt-4 hidden h-2 w-[100px] shrink-0 rounded-full group-data-[vaul-drawer-direction=bottom]/drawer-content:block\" />\n {children}\n </DrawerPrimitive.Content>\n </DrawerPortal>\n )\n}\n\nfunction DrawerHeader({className, ...props}: React.ComponentProps<'div'>) {\n return (\n <div\n data-slot=\"drawer-header\"\n className={cn(\n 'flex flex-col gap-0.5 p-4 group-data-[vaul-drawer-direction=bottom]/drawer-content:text-center group-data-[vaul-drawer-direction=top]/drawer-content:text-center md:gap-1.5 md:text-left',\n className\n )}\n {...props}\n />\n )\n}\n\nfunction DrawerFooter({className, ...props}: React.ComponentProps<'div'>) {\n return (\n <div\n data-slot=\"drawer-footer\"\n className={cn('mt-auto flex flex-col gap-2 p-4', className)}\n {...props}\n />\n )\n}\n\nfunction DrawerTitle({\n className,\n ...props\n}: React.ComponentProps<typeof DrawerPrimitive.Title>) {\n return (\n <DrawerPrimitive.Title\n data-slot=\"drawer-title\"\n className={cn('text-foreground font-semibold', className)}\n {...props}\n />\n )\n}\n\nfunction DrawerDescription({\n className,\n ...props\n}: React.ComponentProps<typeof DrawerPrimitive.Description>) {\n return (\n <DrawerPrimitive.Description\n data-slot=\"drawer-description\"\n className={cn('text-muted-foreground text-sm', className)}\n {...props}\n />\n )\n}\n\nexport {\n Drawer,\n DrawerPortal,\n DrawerOverlay,\n DrawerTrigger,\n DrawerClose,\n DrawerContent,\n DrawerHeader,\n DrawerFooter,\n DrawerTitle,\n DrawerDescription,\n}\n"],"names":["Drawer","props","DrawerPrimitive","DrawerTrigger","DrawerPortal","DrawerClose","DrawerOverlay","className","jsx","cn","DrawerContent","fullHeight","children","jsxs","DrawerHeader","DrawerFooter","DrawerTitle","DrawerDescription"],"mappings":";;;AAMA,SAASA,EAAO,EAAC,GAAGC,KAA2D;AAC7E,2BAAQC,EAAgB,MAAhB,EAAqB,aAAU,UAAU,GAAGD,GAAO;AAC7D;AAEA,SAASE,EAAc;AAAA,EACrB,GAAGF;AACL,GAAyD;AACvD,2BAAQC,EAAgB,SAAhB,EAAwB,aAAU,kBAAkB,GAAGD,GAAO;AACxE;AAEA,SAASG,EAAa;AAAA,EACpB,GAAGH;AACL,GAAwD;AACtD,2BAAQC,EAAgB,QAAhB,EAAuB,aAAU,iBAAiB,GAAGD,GAAO;AACtE;AAEA,SAASI,EAAY;AAAA,EACnB,GAAGJ;AACL,GAAuD;AACrD,2BAAQC,EAAgB,OAAhB,EAAsB,aAAU,gBAAgB,GAAGD,GAAO;AACpE;AAEA,SAASK,EAAc;AAAA,EACrB,WAAAC;AAAA,EACA,GAAGN;AACL,GAAyD;AAErD,SAAA,gBAAAO;AAAA,IAACN,EAAgB;AAAA,IAAhB;AAAA,MACC,aAAU;AAAA,MACV,WAAWO;AAAA,QACT;AAAA,QACAF;AAAA,MACF;AAAA,MACC,GAAGN;AAAA,IAAA;AAAA,EACN;AAEJ;AAEA,SAASS,EAAc;AAAA,EACrB,WAAAH;AAAA,EACA,YAAAI,IAAa;AAAA,EACb,UAAAC;AAAA,EACA,GAAGX;AACL,GAEG;AACD;AAAA;AAAA,IAEG,gBAAAY,EAAAT,GAAA,EACC,UAAA;AAAA,MAAA,gBAAAI,EAACF,GAAc,EAAA;AAAA,MACf,gBAAAO;AAAA,QAACX,EAAgB;AAAA,QAAhB;AAAA,UACC,aAAU;AAAA,UACV,WAAWO;AAAA,YACT;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACAE,IAAa,cAAc;AAAA,YAC3BJ;AAAA,UACF;AAAA,UACC,GAAGN;AAAA,UAEJ,UAAA;AAAA,YAAC,gBAAAO,EAAA,OAAA,EAAI,WAAU,kIAAkI,CAAA;AAAA,YAChJI;AAAA,UAAA;AAAA,QAAA;AAAA,MAAA;AAAA,IACH,EACF,CAAA;AAAA;AAEJ;AAEA,SAASE,EAAa,EAAC,WAAAP,GAAW,GAAGN,KAAqC;AAEtE,SAAA,gBAAAO;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAWC;AAAA,QACT;AAAA,QACAF;AAAA,MACF;AAAA,MACC,GAAGN;AAAA,IAAA;AAAA,EACN;AAEJ;AAEA,SAASc,EAAa,EAAC,WAAAR,GAAW,GAAGN,KAAqC;AAEtE,SAAA,gBAAAO;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAWC,EAAG,mCAAmCF,CAAS;AAAA,MACzD,GAAGN;AAAA,IAAA;AAAA,EACN;AAEJ;AAEA,SAASe,EAAY;AAAA,EACnB,WAAAT;AAAA,EACA,GAAGN;AACL,GAAuD;AAEnD,SAAA,gBAAAO;AAAA,IAACN,EAAgB;AAAA,IAAhB;AAAA,MACC,aAAU;AAAA,MACV,WAAWO,EAAG,iCAAiCF,CAAS;AAAA,MACvD,GAAGN;AAAA,IAAA;AAAA,EACN;AAEJ;AAEA,SAASgB,EAAkB;AAAA,EACzB,WAAAV;AAAA,EACA,GAAGN;AACL,GAA6D;AAEzD,SAAA,gBAAAO;AAAA,IAACN,EAAgB;AAAA,IAAhB;AAAA,MACC,aAAU;AAAA,MACV,WAAWO,EAAG,iCAAiCF,CAAS;AAAA,MACvD,GAAGN;AAAA,IAAA;AAAA,EACN;AAEJ;"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { __module as q } from "../../../../../../../../_virtual/
|
|
1
|
+
import { __module as q } from "../../../../../../../../_virtual/index4.js";
|
|
2
2
|
import { __require as F } from "../../../../../global@4.4.0/node_modules/global/window.js";
|
|
3
3
|
import { __require as N } from "../../../../../@babel_runtime@7.27.6/node_modules/@babel/runtime/helpers/extends.js";
|
|
4
4
|
import { __require as J } from "../../../../../is-function@1.0.2/node_modules/is-function/index.js";
|
|
@@ -2,7 +2,7 @@ import L from "../../../../@videojs_vhs-utils@4.1.1/node_modules/@videojs/vhs-ut
|
|
|
2
2
|
import T from "../../../../../../../_virtual/window.js";
|
|
3
3
|
import { forEachMediaGroup as Z } from "../../../../@videojs_vhs-utils@4.1.1/node_modules/@videojs/vhs-utils/es/media-groups.js";
|
|
4
4
|
import J from "../../../../@videojs_vhs-utils@4.1.1/node_modules/@videojs/vhs-utils/es/decode-b64-to-uint8-array.js";
|
|
5
|
-
import { l as Q } from "../../../../../../../_virtual/
|
|
5
|
+
import { l as Q } from "../../../../../../../_virtual/index5.js";
|
|
6
6
|
/*! @name mpd-parser @version 1.3.1 @license Apache-2.0 */
|
|
7
7
|
const w = (e) => !!e && typeof e == "object", E = (...e) => e.reduce((n, t) => (typeof t != "object" || Object.keys(t).forEach((r) => {
|
|
8
8
|
Array.isArray(n[r]) && Array.isArray(t[r]) ? n[r] = n[r].concat(t[r]) : w(n[r]) && w(t[r]) ? n[r] = E(n[r], t[r]) : n[r] = t[r];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { __module as r } from "../../../../../../../_virtual/
|
|
1
|
+
import { __module as r } from "../../../../../../../_virtual/index11.js";
|
|
2
2
|
import { __require as o } from "../cjs/use-sync-external-store-shim.production.js";
|
|
3
3
|
import { __require as i } from "../cjs/use-sync-external-store-shim.development.js";
|
|
4
4
|
var e;
|
|
@@ -82,6 +82,10 @@ module.exports = {
|
|
|
82
82
|
messages: {
|
|
83
83
|
missingScope:
|
|
84
84
|
'{{source}} requires scope "{{scope}}" in src/manifest.json. Add "{{scope}}" to the "scopes" array.',
|
|
85
|
+
missingScopeProductCard:
|
|
86
|
+
'Component "ProductCard" requires scope "{{scope}}" in src/manifest.json. Add "{{scope}}" to the "scopes" array or set favoriteButtonDisabled to true on all ProductCard instances.',
|
|
87
|
+
missingScopeProductLink:
|
|
88
|
+
'Component "ProductLink" requires scope "{{scope}}" in src/manifest.json. Add "{{scope}}" to the "scopes" array or set hideFavoriteAction to true (or provide a customAction) on all ProductLink instances.',
|
|
85
89
|
missingPermission:
|
|
86
90
|
'{{reason}} requires permission "{{permission}}" in src/manifest.json. Add "{{permission}}" to the "permissions" array.',
|
|
87
91
|
missingTrustedDomain:
|
|
@@ -105,6 +109,8 @@ module.exports = {
|
|
|
105
109
|
const requiredPermissions = new Set()
|
|
106
110
|
const requiredDomains = new Set()
|
|
107
111
|
const fixedIssues = new Set()
|
|
112
|
+
// Track how components are actually used (e.g., with specific props)
|
|
113
|
+
const componentUsagePatterns = new Map()
|
|
108
114
|
|
|
109
115
|
// Check module-level cache first to avoid repeated file I/O
|
|
110
116
|
if (manifestPathCache && fs.existsSync(manifestPathCache)) {
|
|
@@ -361,6 +367,78 @@ module.exports = {
|
|
|
361
367
|
}
|
|
362
368
|
},
|
|
363
369
|
|
|
370
|
+
// Track ProductCard and ProductLink usage with disabled favorite functionality
|
|
371
|
+
JSXElement(node) {
|
|
372
|
+
const elementName = node.openingElement.name.name
|
|
373
|
+
|
|
374
|
+
// Handle ProductCard with favoriteButtonDisabled prop
|
|
375
|
+
if (elementName === 'ProductCard') {
|
|
376
|
+
// Check if favoriteButtonDisabled prop is present and true
|
|
377
|
+
const favoriteDisabledProp = node.openingElement.attributes.find(
|
|
378
|
+
attr =>
|
|
379
|
+
attr.type === 'JSXAttribute' &&
|
|
380
|
+
attr.name?.name === 'favoriteButtonDisabled'
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
const isDisabled =
|
|
384
|
+
favoriteDisabledProp &&
|
|
385
|
+
// Shorthand syntax: <ProductCard favoriteButtonDisabled />
|
|
386
|
+
(favoriteDisabledProp.value === null ||
|
|
387
|
+
// Explicit true: <ProductCard favoriteButtonDisabled={true} />
|
|
388
|
+
(favoriteDisabledProp.value?.type === 'JSXExpressionContainer' &&
|
|
389
|
+
favoriteDisabledProp.value?.expression?.type === 'Literal' &&
|
|
390
|
+
favoriteDisabledProp.value?.expression?.value === true))
|
|
391
|
+
|
|
392
|
+
// Track usage pattern
|
|
393
|
+
const componentPath = 'commerce/product-card'
|
|
394
|
+
if (!componentUsagePatterns.has(componentPath)) {
|
|
395
|
+
componentUsagePatterns.set(componentPath, {
|
|
396
|
+
allDisabled: true,
|
|
397
|
+
hasUsage: true,
|
|
398
|
+
})
|
|
399
|
+
}
|
|
400
|
+
const pattern = componentUsagePatterns.get(componentPath)
|
|
401
|
+
pattern.allDisabled = pattern.allDisabled && isDisabled
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Handle ProductLink with hideFavoriteAction or customAction props
|
|
405
|
+
if (elementName === 'ProductLink') {
|
|
406
|
+
// Check if hideFavoriteAction prop is present and true
|
|
407
|
+
const hideFavoriteProp = node.openingElement.attributes.find(
|
|
408
|
+
attr =>
|
|
409
|
+
attr.type === 'JSXAttribute' &&
|
|
410
|
+
attr.name?.name === 'hideFavoriteAction'
|
|
411
|
+
)
|
|
412
|
+
|
|
413
|
+
// Check if customAction prop is present (replaces favorite action)
|
|
414
|
+
const customActionProp = node.openingElement.attributes.find(
|
|
415
|
+
attr =>
|
|
416
|
+
attr.type === 'JSXAttribute' && attr.name?.name === 'customAction'
|
|
417
|
+
)
|
|
418
|
+
|
|
419
|
+
const isFavoriteDisabled =
|
|
420
|
+
// hideFavoriteAction={true} or shorthand
|
|
421
|
+
(hideFavoriteProp &&
|
|
422
|
+
(hideFavoriteProp.value === null || // shorthand
|
|
423
|
+
(hideFavoriteProp.value?.type === 'JSXExpressionContainer' &&
|
|
424
|
+
hideFavoriteProp.value?.expression?.type === 'Literal' &&
|
|
425
|
+
hideFavoriteProp.value?.expression?.value === true))) ||
|
|
426
|
+
// customAction is provided (any truthy value replaces favorites)
|
|
427
|
+
customActionProp !== undefined
|
|
428
|
+
|
|
429
|
+
// Track usage pattern
|
|
430
|
+
const componentPath = 'commerce/product-link'
|
|
431
|
+
if (!componentUsagePatterns.has(componentPath)) {
|
|
432
|
+
componentUsagePatterns.set(componentPath, {
|
|
433
|
+
allDisabled: true,
|
|
434
|
+
hasUsage: true,
|
|
435
|
+
})
|
|
436
|
+
}
|
|
437
|
+
const pattern = componentUsagePatterns.get(componentPath)
|
|
438
|
+
pattern.allDisabled = pattern.allDisabled && isFavoriteDisabled
|
|
439
|
+
}
|
|
440
|
+
},
|
|
441
|
+
|
|
364
442
|
// Check JSX attributes for external URLs
|
|
365
443
|
JSXAttribute(node) {
|
|
366
444
|
if (!node.value || node.value.type !== 'Literal') {
|
|
@@ -489,6 +567,18 @@ module.exports = {
|
|
|
489
567
|
// Check scopes for components
|
|
490
568
|
usedComponents.forEach(
|
|
491
569
|
({path: componentPath, name: componentName, node}) => {
|
|
570
|
+
// Special handling for components with conditional favorite functionality
|
|
571
|
+
if (
|
|
572
|
+
componentPath === 'commerce/product-card' ||
|
|
573
|
+
componentPath === 'commerce/product-link'
|
|
574
|
+
) {
|
|
575
|
+
const usagePattern = componentUsagePatterns.get(componentPath)
|
|
576
|
+
// Skip scope requirement if all usages have favorites disabled
|
|
577
|
+
if (usagePattern?.hasUsage && usagePattern?.allDisabled) {
|
|
578
|
+
return // No scope required when favorite functionality is disabled
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
|
|
492
582
|
const componentData = componentScopesMap[componentPath]
|
|
493
583
|
if (
|
|
494
584
|
!componentData ||
|
|
@@ -597,9 +687,17 @@ module.exports = {
|
|
|
597
687
|
const sourceName = issue.hookName || issue.componentName
|
|
598
688
|
const sourceType = issue.hookName ? 'Hook' : 'Component'
|
|
599
689
|
|
|
690
|
+
// Use custom message for ProductCard and ProductLink
|
|
691
|
+
let messageId = 'missingScope'
|
|
692
|
+
if (issue.componentName === 'ProductCard') {
|
|
693
|
+
messageId = 'missingScopeProductCard'
|
|
694
|
+
} else if (issue.componentName === 'ProductLink') {
|
|
695
|
+
messageId = 'missingScopeProductLink'
|
|
696
|
+
}
|
|
697
|
+
|
|
600
698
|
context.report({
|
|
601
699
|
loc: {line: 1, column: 0},
|
|
602
|
-
messageId
|
|
700
|
+
messageId,
|
|
603
701
|
data: {
|
|
604
702
|
source: `${sourceType} "${sourceName}"`,
|
|
605
703
|
scope: issue.scope,
|
package/package.json
CHANGED
|
@@ -147,6 +147,33 @@ describe('ProductCard', () => {
|
|
|
147
147
|
|
|
148
148
|
expect(screen.getByText('No Image')).toBeInTheDocument()
|
|
149
149
|
})
|
|
150
|
+
|
|
151
|
+
it('renders favorite button by default', () => {
|
|
152
|
+
const product = mockProduct()
|
|
153
|
+
render(<ProductCard product={product} />)
|
|
154
|
+
|
|
155
|
+
// Favorite button should be present
|
|
156
|
+
const favoriteButton = screen.getByRole('button')
|
|
157
|
+
expect(favoriteButton).toBeInTheDocument()
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
it('hides favorite button when favoriteButtonDisabled is true', () => {
|
|
161
|
+
const product = mockProduct()
|
|
162
|
+
render(<ProductCard product={product} favoriteButtonDisabled />)
|
|
163
|
+
|
|
164
|
+
// Favorite button should not be present
|
|
165
|
+
const favoriteButton = screen.queryByRole('button')
|
|
166
|
+
expect(favoriteButton).not.toBeInTheDocument()
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
it('shows favorite button when favoriteButtonDisabled is false', () => {
|
|
170
|
+
const product = mockProduct()
|
|
171
|
+
render(<ProductCard product={product} favoriteButtonDisabled={false} />)
|
|
172
|
+
|
|
173
|
+
// Favorite button should be present
|
|
174
|
+
const favoriteButton = screen.getByRole('button')
|
|
175
|
+
expect(favoriteButton).toBeInTheDocument()
|
|
176
|
+
})
|
|
150
177
|
})
|
|
151
178
|
|
|
152
179
|
describe('Interactions', () => {
|
|
@@ -262,6 +289,28 @@ describe('ProductCard', () => {
|
|
|
262
289
|
expect(favoriteButton).toHaveClass('bg-button-overlay/30')
|
|
263
290
|
})
|
|
264
291
|
})
|
|
292
|
+
|
|
293
|
+
it('does not allow favorite toggle when favoriteButtonDisabled is true', async () => {
|
|
294
|
+
const product = mockProduct({isFavorited: false})
|
|
295
|
+
const onFavoriteToggled = vi.fn()
|
|
296
|
+
|
|
297
|
+
render(
|
|
298
|
+
<ProductCard
|
|
299
|
+
product={product}
|
|
300
|
+
favoriteButtonDisabled
|
|
301
|
+
onFavoriteToggled={onFavoriteToggled}
|
|
302
|
+
/>
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
// Button should not exist at all
|
|
306
|
+
const favoriteButton = screen.queryByRole('button')
|
|
307
|
+
expect(favoriteButton).not.toBeInTheDocument()
|
|
308
|
+
|
|
309
|
+
// Callbacks should not be called
|
|
310
|
+
expect(onFavoriteToggled).not.toHaveBeenCalled()
|
|
311
|
+
expect(mockMinisSDK.saveProduct).not.toHaveBeenCalled()
|
|
312
|
+
expect(mockMinisSDK.unsaveProduct).not.toHaveBeenCalled()
|
|
313
|
+
})
|
|
265
314
|
})
|
|
266
315
|
|
|
267
316
|
describe('Custom Composition', () => {
|
|
@@ -307,6 +356,25 @@ describe('ProductCard', () => {
|
|
|
307
356
|
expect(screen.getByText(product.title)).toBeInTheDocument()
|
|
308
357
|
expect(screen.getByText('$99.99')).toBeInTheDocument()
|
|
309
358
|
})
|
|
359
|
+
|
|
360
|
+
it('respects favoriteButtonDisabled in custom composition', () => {
|
|
361
|
+
const product = mockProduct()
|
|
362
|
+
|
|
363
|
+
render(
|
|
364
|
+
<ProductCard product={product} favoriteButtonDisabled>
|
|
365
|
+
<ProductCardContainer>
|
|
366
|
+
<ProductCardImageContainer>
|
|
367
|
+
<ProductCardImage />
|
|
368
|
+
<ProductCardFavoriteButton />
|
|
369
|
+
</ProductCardImageContainer>
|
|
370
|
+
</ProductCardContainer>
|
|
371
|
+
</ProductCard>
|
|
372
|
+
)
|
|
373
|
+
|
|
374
|
+
// Favorite button should not be rendered even when explicitly included
|
|
375
|
+
const favoriteButton = screen.queryByRole('button')
|
|
376
|
+
expect(favoriteButton).not.toBeInTheDocument()
|
|
377
|
+
})
|
|
310
378
|
})
|
|
311
379
|
|
|
312
380
|
describe('Edge Cases', () => {
|
|
@@ -28,6 +28,7 @@ interface ProductCardContextValue {
|
|
|
28
28
|
|
|
29
29
|
// State
|
|
30
30
|
isFavorited: boolean
|
|
31
|
+
isFavoriteButtonDisabled: boolean
|
|
31
32
|
|
|
32
33
|
// Actions
|
|
33
34
|
onClick: () => void
|
|
@@ -194,7 +195,10 @@ function ProductCardFavoriteButton({
|
|
|
194
195
|
className,
|
|
195
196
|
...props
|
|
196
197
|
}: React.ComponentProps<'div'>) {
|
|
197
|
-
const {isFavorited, onFavoriteToggle} =
|
|
198
|
+
const {isFavorited, isFavoriteButtonDisabled, onFavoriteToggle} =
|
|
199
|
+
useProductCardContext()
|
|
200
|
+
if (isFavoriteButtonDisabled) return null
|
|
201
|
+
|
|
198
202
|
return (
|
|
199
203
|
<div className={cn('absolute bottom-3 right-3 z-10', className)} {...props}>
|
|
200
204
|
<FavoriteButton onClick={onFavoriteToggle} filled={isFavorited} />
|
|
@@ -292,6 +296,8 @@ export interface ProductCardProps {
|
|
|
292
296
|
onFavoriteToggled?: (isFavorited: boolean) => void
|
|
293
297
|
/** Custom layout via children */
|
|
294
298
|
children?: React.ReactNode
|
|
299
|
+
/** Whether the favorite button is disabled */
|
|
300
|
+
favoriteButtonDisabled?: boolean
|
|
295
301
|
}
|
|
296
302
|
|
|
297
303
|
function ProductCard({
|
|
@@ -304,6 +310,7 @@ function ProductCard({
|
|
|
304
310
|
onProductClick,
|
|
305
311
|
onFavoriteToggled,
|
|
306
312
|
children,
|
|
313
|
+
favoriteButtonDisabled = false,
|
|
307
314
|
}: ProductCardProps) {
|
|
308
315
|
const {navigateToProduct} = useShopNavigation()
|
|
309
316
|
const {saveProduct, unsaveProduct} = useSavedProductsActions()
|
|
@@ -374,7 +381,7 @@ function ProductCard({
|
|
|
374
381
|
|
|
375
382
|
// State
|
|
376
383
|
isFavorited: isFavoritedLocal,
|
|
377
|
-
|
|
384
|
+
isFavoriteButtonDisabled: favoriteButtonDisabled,
|
|
378
385
|
// Actions
|
|
379
386
|
onClick: handleClick,
|
|
380
387
|
onFavoriteToggle: handleFavoriteClick,
|
|
@@ -389,6 +396,7 @@ function ProductCard({
|
|
|
389
396
|
isFavoritedLocal,
|
|
390
397
|
handleClick,
|
|
391
398
|
handleFavoriteClick,
|
|
399
|
+
favoriteButtonDisabled,
|
|
392
400
|
]
|
|
393
401
|
)
|
|
394
402
|
|
|
@@ -184,6 +184,53 @@ describe('ProductLink', () => {
|
|
|
184
184
|
// Should not have favorite button
|
|
185
185
|
expect(screen.queryByRole('button')).not.toBeInTheDocument()
|
|
186
186
|
})
|
|
187
|
+
|
|
188
|
+
it('renders custom action when provided', () => {
|
|
189
|
+
const product = mockProduct()
|
|
190
|
+
const onCustomActionClick = vi.fn()
|
|
191
|
+
render(
|
|
192
|
+
<ProductLink
|
|
193
|
+
product={product}
|
|
194
|
+
customAction={
|
|
195
|
+
<button type="button" data-testid="custom-cta">
|
|
196
|
+
Add to Cart
|
|
197
|
+
</button>
|
|
198
|
+
}
|
|
199
|
+
onCustomActionClick={onCustomActionClick}
|
|
200
|
+
/>
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
// Custom action should be rendered
|
|
204
|
+
expect(screen.getByTestId('custom-cta')).toBeInTheDocument()
|
|
205
|
+
expect(screen.getByText('Add to Cart')).toBeInTheDocument()
|
|
206
|
+
|
|
207
|
+
// Favorite button should not be rendered when custom action is present
|
|
208
|
+
// Check that there's only one button (the custom one)
|
|
209
|
+
const buttons = screen.getAllByRole('button')
|
|
210
|
+
expect(buttons).toHaveLength(1)
|
|
211
|
+
expect(buttons[0]).toHaveAttribute('data-testid', 'custom-cta')
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
it('renders custom action even when hideFavoriteAction is true', () => {
|
|
215
|
+
const product = mockProduct()
|
|
216
|
+
const onCustomActionClick = vi.fn()
|
|
217
|
+
render(
|
|
218
|
+
<ProductLink
|
|
219
|
+
product={product}
|
|
220
|
+
hideFavoriteAction
|
|
221
|
+
customAction={
|
|
222
|
+
<button type="button" data-testid="custom-action">
|
|
223
|
+
Buy Now
|
|
224
|
+
</button>
|
|
225
|
+
}
|
|
226
|
+
onCustomActionClick={onCustomActionClick}
|
|
227
|
+
/>
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
// Custom action should still be rendered
|
|
231
|
+
expect(screen.getByTestId('custom-action')).toBeInTheDocument()
|
|
232
|
+
expect(screen.getByText('Buy Now')).toBeInTheDocument()
|
|
233
|
+
})
|
|
187
234
|
})
|
|
188
235
|
|
|
189
236
|
describe('Interactions', () => {
|
|
@@ -313,6 +360,40 @@ describe('ProductLink', () => {
|
|
|
313
360
|
expect(onClick).not.toHaveBeenCalled()
|
|
314
361
|
expect(mockMinisSDK.navigateToProduct).not.toHaveBeenCalled()
|
|
315
362
|
})
|
|
363
|
+
|
|
364
|
+
it('calls onCustomActionClick when custom action is clicked', async () => {
|
|
365
|
+
const user = userEvent.setup()
|
|
366
|
+
const product = mockProduct()
|
|
367
|
+
const onClick = vi.fn()
|
|
368
|
+
const onCustomActionClick = vi.fn()
|
|
369
|
+
|
|
370
|
+
render(
|
|
371
|
+
<ProductLink
|
|
372
|
+
product={product}
|
|
373
|
+
onClick={onClick}
|
|
374
|
+
customAction={
|
|
375
|
+
<button type="button" data-testid="custom-btn">
|
|
376
|
+
Quick Add
|
|
377
|
+
</button>
|
|
378
|
+
}
|
|
379
|
+
onCustomActionClick={onCustomActionClick}
|
|
380
|
+
/>
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
const customButton = screen.getByTestId('custom-btn')
|
|
384
|
+
await user.click(customButton)
|
|
385
|
+
|
|
386
|
+
// Should call custom action handler
|
|
387
|
+
expect(onCustomActionClick).toHaveBeenCalledTimes(1)
|
|
388
|
+
|
|
389
|
+
// Should NOT call favorite save/unsave
|
|
390
|
+
expect(mockMinisSDK.saveProduct).not.toHaveBeenCalled()
|
|
391
|
+
expect(mockMinisSDK.unsaveProduct).not.toHaveBeenCalled()
|
|
392
|
+
|
|
393
|
+
// Should NOT trigger product navigation
|
|
394
|
+
expect(onClick).not.toHaveBeenCalled()
|
|
395
|
+
expect(mockMinisSDK.navigateToProduct).not.toHaveBeenCalled()
|
|
396
|
+
})
|
|
316
397
|
})
|
|
317
398
|
|
|
318
399
|
describe('Star Rating Display', () => {
|