@thorprovider/blocks 0.1.3 → 1.0.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/CHANGELOG.md +107 -0
- package/README.md +73 -29
- package/docs/standards/data-contracts.md +13 -2
- package/package.json +26 -10
- package/scripts/fix-esm-extensions.mjs +40 -0
- package/src/blocks/e-commerce/product-categories/category-card-grid.test.tsx +122 -0
- package/src/blocks/e-commerce/product-categories/category-card.test.tsx +68 -0
- package/src/blocks/e-commerce/product-categories/category-card.tsx +172 -0
- package/src/blocks/e-commerce/product-categories/category-grid.test.tsx +67 -0
- package/src/blocks/e-commerce/product-categories/category-shared.tsx +109 -0
- package/src/blocks/e-commerce/product-categories/product-category-grid.tsx +170 -0
- package/src/blocks/e-commerce/product-categories/product-category.test.tsx +266 -0
- package/src/blocks/e-commerce/product-categories/product-category.tsx +462 -0
- package/src/blocks/e-commerce/product-list/product-card.test.tsx +263 -0
- package/src/blocks/e-commerce/product-list/product-card.tsx +424 -0
- package/src/blocks/e-commerce/product-list/product-grid.test.tsx +162 -0
- package/src/blocks/e-commerce/product-list/product-grid.tsx +114 -0
- package/src/blocks/e-commerce/product-overview/product-overview-gallery.tsx +71 -0
- package/src/blocks/e-commerce/product-overview/{product-overview-5-lightbox-tab.tsx → product-overview-lightbox-tab.tsx} +35 -23
- package/src/blocks/e-commerce/product-overview/{product-overview-5-reviews-tab.tsx → product-overview-reviews-tab.tsx} +40 -17
- package/src/blocks/e-commerce/product-overview/{product-overview-5-tabs.tsx → product-overview-tabs.tsx} +8 -7
- package/src/blocks/e-commerce/product-overview/product-overview.test.tsx +283 -0
- package/src/blocks/e-commerce/product-overview/product-overview.tsx +1186 -0
- package/src/blocks/e-commerce/reviews/review-rating-2.tsx +5 -6
- package/src/blocks/e-commerce/shopping-carts/shopping-cart.test.tsx +399 -0
- package/src/blocks/e-commerce/shopping-carts/{shopping-cart-4.tsx → shopping-cart.tsx} +119 -85
- package/src/blocks/e-commerce/storefront-hero/storefront-hero.test.tsx +412 -0
- package/src/blocks/e-commerce/storefront-hero/storefront-hero.tsx +1283 -0
- package/src/blocks/marketing/features/feature.test.tsx +153 -0
- package/src/blocks/marketing/features/feature.tsx +195 -0
- package/src/blocks/marketing/navbars/navbar-4-account-menu.test.tsx +27 -9
- package/src/blocks/marketing/navbars/navbar-4.test.tsx +33 -2
- package/src/blocks/marketing/navbars/navbar-4.tsx +29 -11
- package/src/index.ts +77 -48
- package/src/lib/contracts-curated.test.ts +42 -24
- package/src/lib/contracts.test.ts +143 -2
- package/src/lib/contracts.ts +346 -14
- package/src/ui/button-group.tsx +1 -1
- package/src/ui/carousel.tsx +1 -1
- package/src/ui/dialog.tsx +1 -1
- package/src/ui/field.tsx +2 -2
- package/src/ui/input-group.tsx +3 -3
- package/src/ui/pagination.tsx +1 -1
- package/src/ui/sidebar.tsx +7 -7
- package/src/ui/toggle-group.tsx +1 -1
- package/tsconfig.build.json +12 -0
- package/tsup.config.ts +11 -7
- package/.turbo/turbo-build.log +0 -24
- package/.turbo/turbo-test.log +0 -47
- package/.turbo/turbo-type-check.log +0 -0
- package/dist/.tsbuildinfo +0 -1
- package/dist/contracts.d.mts +0 -1732
- package/dist/contracts.d.ts +0 -1732
- package/dist/contracts.js +0 -423
- package/dist/contracts.js.map +0 -1
- package/dist/contracts.mjs +0 -365
- package/dist/contracts.mjs.map +0 -1
- package/dist/index.d.mts +0 -3068
- package/dist/index.d.ts +0 -3068
- package/dist/index.js +0 -17834
- package/dist/index.js.map +0 -1
- package/dist/index.mjs +0 -17740
- package/dist/index.mjs.map +0 -1
- package/src/blocks/e-commerce/product-categories/avatar-group.tsx +0 -86
- package/src/blocks/e-commerce/product-categories/product-category-1.tsx +0 -126
- package/src/blocks/e-commerce/product-categories/product-category-2.test.tsx +0 -51
- package/src/blocks/e-commerce/product-categories/product-category-2.tsx +0 -120
- package/src/blocks/e-commerce/product-categories/product-category-4.tsx +0 -129
- package/src/blocks/e-commerce/product-categories/product-category-5.tsx +0 -218
- package/src/blocks/e-commerce/product-list/product-card-1.test.tsx +0 -88
- package/src/blocks/e-commerce/product-list/product-card-1.tsx +0 -192
- package/src/blocks/e-commerce/product-list/product-card-2.tsx +0 -71
- package/src/blocks/e-commerce/product-list/product-card-3-reaction-toggle.tsx +0 -166
- package/src/blocks/e-commerce/product-list/product-card-3.tsx +0 -215
- package/src/blocks/e-commerce/product-list/product-card-5-checkbox-check.tsx +0 -98
- package/src/blocks/e-commerce/product-list/product-card-5-reaction-toggle.tsx +0 -171
- package/src/blocks/e-commerce/product-list/product-card-5.tsx +0 -367
- package/src/blocks/e-commerce/product-overview/product-overview-1.test.tsx +0 -182
- package/src/blocks/e-commerce/product-overview/product-overview-1.tsx +0 -370
- package/src/blocks/e-commerce/product-overview/product-overview-2.tsx +0 -272
- package/src/blocks/e-commerce/product-overview/product-overview-5-gallery.tsx +0 -59
- package/src/blocks/e-commerce/product-overview/product-overview-5.tsx +0 -274
- package/src/blocks/e-commerce/shopping-carts/shopping-cart-1-spinning-counter.tsx +0 -142
- package/src/blocks/e-commerce/shopping-carts/shopping-cart-1.tsx +0 -291
- package/src/blocks/e-commerce/shopping-carts/shopping-cart-2-item-row.tsx +0 -110
- package/src/blocks/e-commerce/shopping-carts/shopping-cart-2-recommendations.tsx +0 -51
- package/src/blocks/e-commerce/shopping-carts/shopping-cart-2-spinning-counter.tsx +0 -146
- package/src/blocks/e-commerce/shopping-carts/shopping-cart-2.tsx +0 -295
- package/src/blocks/e-commerce/shopping-carts/shopping-cart-3-spinning-counter.tsx +0 -142
- package/src/blocks/e-commerce/shopping-carts/shopping-cart-3.tsx +0 -342
- package/src/blocks/e-commerce/shopping-carts/shopping-cart-4-spinning-counter.tsx +0 -146
- package/src/blocks/e-commerce/shopping-carts/shopping-cart-4.test.tsx +0 -229
- package/src/blocks/e-commerce/storefront-hero/storefront-hero-1.test.tsx +0 -72
- package/src/blocks/e-commerce/storefront-hero/storefront-hero-1.tsx +0 -263
- package/src/blocks/e-commerce/storefront-hero/storefront-hero-2.tsx +0 -283
- package/src/blocks/e-commerce/storefront-hero/storefront-hero-5.tsx +0 -318
- package/src/blocks/e-commerce/storefront-hero/storefront-hero-6.tsx +0 -205
- package/src/blocks/marketing/features/feature-1.tsx +0 -97
- package/src/blocks/marketing/features/feature-2.tsx +0 -82
package/src/index.ts
CHANGED
|
@@ -23,16 +23,18 @@ export { default as Navbar4 } from './blocks/marketing/navbars/navbar-4'
|
|
|
23
23
|
export { default as Banner1 } from './blocks/marketing/banners/banner-1'
|
|
24
24
|
export { default as HeroSection1 } from './blocks/marketing/hero-sections/hero-section-1'
|
|
25
25
|
export { default as CtaSection1 } from './blocks/marketing/cta/cta-section-1'
|
|
26
|
-
export { default as
|
|
27
|
-
export { default as Feature2 } from './blocks/marketing/features/feature-2'
|
|
26
|
+
export { default as Feature } from './blocks/marketing/features/feature'
|
|
28
27
|
export { default as Testimonial1 } from './blocks/marketing/testimonials/testimonial-1'
|
|
29
28
|
export { default as Testimonial2 } from './blocks/marketing/testimonials/testimonial-2'
|
|
30
29
|
export { default as FaqBlock1 } from './blocks/marketing/faq/faq-block-1'
|
|
31
30
|
export { default as PricingSection1 } from './blocks/marketing/pricing/pricing-section-1'
|
|
32
31
|
export type { CtaSection1Props } from './blocks/marketing/cta/cta-section-1'
|
|
33
32
|
export type { FaqBlock1Props, FaqItem } from './blocks/marketing/faq/faq-block-1'
|
|
34
|
-
export type {
|
|
35
|
-
|
|
33
|
+
export type {
|
|
34
|
+
FeatureProps,
|
|
35
|
+
FeatureItem,
|
|
36
|
+
FeaturePresentation,
|
|
37
|
+
} from './blocks/marketing/features/feature'
|
|
36
38
|
export type { LogoCloud1Props, LogoCloud1Platform } from './blocks/marketing/logo-clouds/logo-cloud-1'
|
|
37
39
|
export type { PricingSection1Props, PricingTier } from './blocks/marketing/pricing/pricing-section-1'
|
|
38
40
|
export type { Statistic1Props, StatisticItem } from './blocks/marketing/statistics/statistic-1'
|
|
@@ -52,41 +54,43 @@ export type {
|
|
|
52
54
|
FooterSection4SocialIcon,
|
|
53
55
|
FooterSection4SocialLink,
|
|
54
56
|
} from './blocks/marketing/footers/footer-section-4'
|
|
55
|
-
export {
|
|
56
|
-
export { default as ProductCard2 } from './blocks/e-commerce/product-list/product-card-2'
|
|
57
|
-
export { default as ProductCard3 } from './blocks/e-commerce/product-list/product-card-3'
|
|
58
|
-
export { default as ProductCard5 } from './blocks/e-commerce/product-list/product-card-5'
|
|
59
|
-
export { default as ProductShowcase1 } from './blocks/e-commerce/product-list/product-showcase-1'
|
|
57
|
+
export { ProductCard } from './blocks/e-commerce/product-list/product-card'
|
|
60
58
|
export type {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
} from './blocks/e-commerce/product-list/product-card
|
|
59
|
+
ProductCardProps,
|
|
60
|
+
ProductCardPresentation,
|
|
61
|
+
} from './blocks/e-commerce/product-list/product-card'
|
|
62
|
+
export { ProductGrid } from './blocks/e-commerce/product-list/product-grid'
|
|
63
|
+
export type { ProductGridProps } from './blocks/e-commerce/product-list/product-grid'
|
|
64
|
+
export { default as ProductShowcase1 } from './blocks/e-commerce/product-list/product-showcase-1'
|
|
64
65
|
export type { ProductShowcase1Props } from './blocks/e-commerce/product-list/product-showcase-1'
|
|
65
|
-
export { default as
|
|
66
|
+
export { default as ProductOverview } from './blocks/e-commerce/product-overview/product-overview'
|
|
66
67
|
export type {
|
|
67
|
-
|
|
68
|
-
|
|
68
|
+
ProductOverviewProps,
|
|
69
|
+
ProductOverviewRating,
|
|
70
|
+
ProductOverviewPresentation,
|
|
71
|
+
ProductOverviewReview,
|
|
69
72
|
ProductDetailProduct,
|
|
70
73
|
ProductDetailVariant,
|
|
71
|
-
} from './blocks/e-commerce/product-overview/product-overview
|
|
72
|
-
export { default as ProductOverview2 } from './blocks/e-commerce/product-overview/product-overview-2'
|
|
73
|
-
export { default as ProductOverview5 } from './blocks/e-commerce/product-overview/product-overview-5'
|
|
74
|
-
export type { ProductOverview2Props, ProductOverview2Product } from './blocks/e-commerce/product-overview/product-overview-2'
|
|
75
|
-
export type { ProductOverview5Props, ProductOverview5Product, ProductOverview5Review } from './blocks/e-commerce/product-overview/product-overview-5'
|
|
74
|
+
} from './blocks/e-commerce/product-overview/product-overview'
|
|
76
75
|
export { default as ProductQuickView } from './blocks/e-commerce/product-overview/product-quick-view'
|
|
77
76
|
export { default as ReviewRating1 } from './blocks/e-commerce/reviews/review-rating-1'
|
|
78
77
|
export type { ReviewRating1Review } from './blocks/e-commerce/reviews/review-rating-1'
|
|
79
78
|
export { default as ReviewRating2 } from './blocks/e-commerce/reviews/review-rating-2'
|
|
80
|
-
export { default as
|
|
79
|
+
export { default as ProductCategory } from './blocks/e-commerce/product-categories/product-category'
|
|
81
80
|
export type {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
} from './blocks/e-commerce/product-categories/
|
|
86
|
-
export {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
81
|
+
ProductCategoryProps,
|
|
82
|
+
ProductCategoryPresentation,
|
|
83
|
+
} from './blocks/e-commerce/product-categories/product-category'
|
|
84
|
+
export { default as CategoryCard } from './blocks/e-commerce/product-categories/category-card'
|
|
85
|
+
export type {
|
|
86
|
+
CategoryCardProps,
|
|
87
|
+
CategoryCardPresentation,
|
|
88
|
+
} from './blocks/e-commerce/product-categories/category-card'
|
|
89
|
+
export { default as CategoryGrid } from './blocks/e-commerce/product-categories/product-category-grid'
|
|
90
|
+
export type {
|
|
91
|
+
CategoryGridProps,
|
|
92
|
+
CategoryGridPresentation,
|
|
93
|
+
} from './blocks/e-commerce/product-categories/product-category-grid'
|
|
90
94
|
export { MegaMenu, megaMenuPropsSchema } from './blocks/e-commerce/navigation/mega-menu'
|
|
91
95
|
export type { MegaMenuProps } from './blocks/e-commerce/navigation/mega-menu'
|
|
92
96
|
export { MobileCategoryDrawer, mobileCategoryDrawerPropsSchema } from './blocks/e-commerce/navigation/mobile-category-drawer'
|
|
@@ -128,13 +132,13 @@ export type {
|
|
|
128
132
|
} from './blocks/e-commerce/category-filters/category-filter-2'
|
|
129
133
|
export { default as CategoryFilter4 } from './blocks/e-commerce/category-filters/category-filter-4'
|
|
130
134
|
export type { CategoryFilter4Props, CategoryFilter4Product, CategoryFilter4Facets } from './blocks/e-commerce/category-filters/category-filter-4'
|
|
131
|
-
export { default as
|
|
132
|
-
export {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
135
|
+
export { default as ShoppingCart } from './blocks/e-commerce/shopping-carts/shopping-cart'
|
|
136
|
+
export type {
|
|
137
|
+
ShoppingCartProps,
|
|
138
|
+
ShoppingCartPromotion,
|
|
139
|
+
ShoppingCartPresentation,
|
|
140
|
+
ShoppingCartTotalsState,
|
|
141
|
+
} from './blocks/e-commerce/shopping-carts/shopping-cart'
|
|
138
142
|
export { default as StoreContainer1 } from './blocks/e-commerce/store-containers/store-container-1'
|
|
139
143
|
export type { StoreContainer1Props } from './blocks/e-commerce/store-containers/store-container-1'
|
|
140
144
|
export { default as CheckoutForm1 } from './blocks/e-commerce/checkout-forms/checkout-form-1'
|
|
@@ -181,14 +185,18 @@ export type {
|
|
|
181
185
|
export type { CheckoutFieldError, CheckoutFieldErrors } from './blocks/e-commerce/checkout-forms/checkout-form-types'
|
|
182
186
|
export { default as OrderHistory1 } from './blocks/e-commerce/order-history/order-history-1'
|
|
183
187
|
export { default as OrderHistory2 } from './blocks/e-commerce/order-history/order-history-2'
|
|
184
|
-
export { default as
|
|
188
|
+
export { default as StorefrontHero } from './blocks/e-commerce/storefront-hero/storefront-hero'
|
|
185
189
|
export type {
|
|
186
190
|
FeaturedProduct,
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
191
|
+
HeroImage,
|
|
192
|
+
HeroDepartment,
|
|
193
|
+
HeroFeaturedDeal,
|
|
194
|
+
HeroStoreLocator,
|
|
195
|
+
HeroDeal,
|
|
196
|
+
HeroCategory,
|
|
197
|
+
StorefrontHeroPresentation,
|
|
198
|
+
StorefrontHeroProps,
|
|
199
|
+
} from './blocks/e-commerce/storefront-hero/storefront-hero'
|
|
192
200
|
export { default as AppShell1 } from './blocks/application/app-shells/app-shell-1'
|
|
193
201
|
export { default as LoginPage1 } from './blocks/application/login-forms/login-page-1'
|
|
194
202
|
export { default as LoginPage2 } from './blocks/application/login-forms/login-page-2'
|
|
@@ -215,13 +223,18 @@ export {
|
|
|
215
223
|
cartTotalsSchema,
|
|
216
224
|
productDetailProductSchema,
|
|
217
225
|
productDetailVariantSchema,
|
|
218
|
-
|
|
226
|
+
productOverviewPropsSchema,
|
|
227
|
+
storefrontHeroPropsSchema,
|
|
228
|
+
storefrontHeroProductSchema,
|
|
219
229
|
buttonPropsSchema,
|
|
220
230
|
storeContainer1PropsSchema,
|
|
221
|
-
|
|
231
|
+
productCardPropsSchema,
|
|
232
|
+
productGridPropsSchema,
|
|
222
233
|
productPresentationSchema,
|
|
223
234
|
productShowcase1PropsSchema,
|
|
224
|
-
|
|
235
|
+
shoppingCartPropsSchema,
|
|
236
|
+
shoppingCartItemSchema,
|
|
237
|
+
shoppingCartSuggestionSchema,
|
|
225
238
|
navbar4PropsSchema,
|
|
226
239
|
paymentMethodSchema,
|
|
227
240
|
cardProductSchema,
|
|
@@ -232,18 +245,27 @@ export {
|
|
|
232
245
|
navCartItemSchema,
|
|
233
246
|
storeUserSchema,
|
|
234
247
|
accountMenuItemSchema,
|
|
248
|
+
featurePropsSchema,
|
|
249
|
+
featureCardsSpecSchema,
|
|
250
|
+
featureSplitSpecSchema,
|
|
251
|
+
productCategoryItemSchema,
|
|
252
|
+
productCategoryPropsSchema,
|
|
235
253
|
} from './lib/contracts'
|
|
236
254
|
export type {
|
|
237
255
|
ProductDetailProductSpec,
|
|
238
256
|
ProductDetailVariantSpec,
|
|
239
|
-
|
|
257
|
+
StorefrontHeroProduct,
|
|
258
|
+
StorefrontHeroSpecProps,
|
|
240
259
|
CartTotals,
|
|
241
260
|
ButtonSpecProps,
|
|
242
261
|
StoreContainer1SpecProps,
|
|
243
|
-
|
|
262
|
+
ProductCardSpecProps,
|
|
263
|
+
ProductGridSpecProps,
|
|
244
264
|
ProductPresentation,
|
|
245
265
|
ProductShowcase1SpecProps,
|
|
246
|
-
|
|
266
|
+
ShoppingCartItem,
|
|
267
|
+
ShoppingCartSuggestion,
|
|
268
|
+
ShoppingCartSpecProps,
|
|
247
269
|
Navbar4SpecProps,
|
|
248
270
|
CardProduct,
|
|
249
271
|
CartItem,
|
|
@@ -253,4 +275,11 @@ export type {
|
|
|
253
275
|
NavCartItem,
|
|
254
276
|
StoreUser,
|
|
255
277
|
AccountMenuItem,
|
|
278
|
+
FeatureSpecProps,
|
|
279
|
+
FeatureCardsSpecProps,
|
|
280
|
+
FeatureSplitSpecProps,
|
|
281
|
+
ProductCategoryItem,
|
|
282
|
+
ProductCategorySpecProps,
|
|
283
|
+
CategoryCardSpecProps,
|
|
284
|
+
CategoryGridSpecProps,
|
|
256
285
|
} from './lib/contracts'
|
|
@@ -11,8 +11,8 @@ import {
|
|
|
11
11
|
moneySchema,
|
|
12
12
|
navCartItemSchema,
|
|
13
13
|
navbar4PropsSchema,
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
productOverviewPropsSchema,
|
|
15
|
+
shoppingCartPropsSchema,
|
|
16
16
|
} from './contracts'
|
|
17
17
|
|
|
18
18
|
const product = {
|
|
@@ -55,13 +55,13 @@ describe('moneySchema', () => {
|
|
|
55
55
|
})
|
|
56
56
|
})
|
|
57
57
|
|
|
58
|
-
describe('
|
|
58
|
+
describe('productOverviewPropsSchema', () => {
|
|
59
59
|
it('accepts a product whose variant price is omitted or null (unavailable)', () => {
|
|
60
|
-
expect(
|
|
60
|
+
expect(productOverviewPropsSchema.safeParse({ product }).success).toBe(true)
|
|
61
61
|
})
|
|
62
62
|
|
|
63
63
|
it('accepts an explicit controlled selected variant id', () => {
|
|
64
|
-
expect(
|
|
64
|
+
expect(productOverviewPropsSchema.safeParse({ product, selectedVariantId: 'v1' }).success).toBe(true)
|
|
65
65
|
})
|
|
66
66
|
|
|
67
67
|
it('rejects a variant missing selectedOptions', () => {
|
|
@@ -69,7 +69,7 @@ describe('productOverview1PropsSchema', () => {
|
|
|
69
69
|
...product,
|
|
70
70
|
variants: [{ id: 'v1', title: 'x', availableForSale: true, price: null }],
|
|
71
71
|
}
|
|
72
|
-
expect(
|
|
72
|
+
expect(productOverviewPropsSchema.safeParse({ product: bad }).success).toBe(false)
|
|
73
73
|
})
|
|
74
74
|
})
|
|
75
75
|
|
|
@@ -147,24 +147,6 @@ describe('optional brand seams — present, omitted, invalid', () => {
|
|
|
147
147
|
})
|
|
148
148
|
})
|
|
149
149
|
|
|
150
|
-
describe('shoppingCart4PropsSchema — authoritative totals', () => {
|
|
151
|
-
it('accepts backend totals and a controlled totals state', () => {
|
|
152
|
-
const result = shoppingCart4PropsSchema.safeParse({
|
|
153
|
-
items: [],
|
|
154
|
-
totals: {
|
|
155
|
-
subtotal: { amount: '0', currencyCode: 'USD' },
|
|
156
|
-
total: { amount: '0', currencyCode: 'USD' },
|
|
157
|
-
},
|
|
158
|
-
totalsState: 'idle',
|
|
159
|
-
})
|
|
160
|
-
expect(result.success).toBe(true)
|
|
161
|
-
})
|
|
162
|
-
|
|
163
|
-
it('rejects an unknown totals state', () => {
|
|
164
|
-
expect(shoppingCart4PropsSchema.safeParse({ items: [], totalsState: 'loading' }).success).toBe(false)
|
|
165
|
-
})
|
|
166
|
-
})
|
|
167
|
-
|
|
168
150
|
describe('faqBlock1PropsSchema / ctaSection1PropsSchema — bounded editorial sections', () => {
|
|
169
151
|
const faqItem = { value: 'q1', question: '¿Envían a todo el país?', answer: 'Sí.' }
|
|
170
152
|
|
|
@@ -205,3 +187,39 @@ describe('faqBlock1PropsSchema / ctaSection1PropsSchema — bounded editorial se
|
|
|
205
187
|
expect('onPrimaryClick' in parsed).toBe(false)
|
|
206
188
|
})
|
|
207
189
|
})
|
|
190
|
+
|
|
191
|
+
describe('shoppingCartPropsSchema — canonical Money-only cart lines', () => {
|
|
192
|
+
const money = { amount: '22.22', currencyCode: 'USD' }
|
|
193
|
+
|
|
194
|
+
it('accepts a canonical Money line keyed by backend line id', () => {
|
|
195
|
+
const result = shoppingCartPropsSchema.safeParse({
|
|
196
|
+
items: [{ id: 'line_1', name: 'Teclado', quantity: 2, unitPrice: money, lineTotal: money }],
|
|
197
|
+
totals: { subtotal: money, total: money },
|
|
198
|
+
totalsState: 'idle',
|
|
199
|
+
})
|
|
200
|
+
expect(result.success).toBe(true)
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
it('rejects a legacy numeric-price line without canonical Money', () => {
|
|
204
|
+
const result = shoppingCartPropsSchema.safeParse({
|
|
205
|
+
items: [{ id: 'line_1', name: 'Teclado', price: 10, quantity: 2 }],
|
|
206
|
+
})
|
|
207
|
+
expect(result.success).toBe(false)
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
it('rejects an unknown totals state', () => {
|
|
211
|
+
const result = shoppingCartPropsSchema.safeParse({
|
|
212
|
+
items: [],
|
|
213
|
+
totalsState: 'loading',
|
|
214
|
+
})
|
|
215
|
+
expect(result.success).toBe(false)
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
it('omits an absent suggestion image and price honestly', () => {
|
|
219
|
+
const result = shoppingCartPropsSchema.safeParse({
|
|
220
|
+
items: [{ id: 'line_1', name: 'x', quantity: 1, unitPrice: money, lineTotal: money }],
|
|
221
|
+
recentlyViewed: [{ id: 'p1', name: 'Mouse' }],
|
|
222
|
+
})
|
|
223
|
+
expect(result.success).toBe(true)
|
|
224
|
+
})
|
|
225
|
+
})
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest'
|
|
2
|
-
import {
|
|
2
|
+
import { z } from 'zod'
|
|
3
|
+
import {
|
|
4
|
+
categoryCardPropsSchema,
|
|
5
|
+
categoryGridPropsSchema,
|
|
6
|
+
productCardPropsSchema,
|
|
7
|
+
productGridPropsSchema,
|
|
8
|
+
storeCategorySchema,
|
|
9
|
+
} from './contracts'
|
|
3
10
|
|
|
4
11
|
describe('storeCategorySchema', () => {
|
|
5
12
|
it('accepts flat categories (additive backward compatibility)', () => {
|
|
@@ -29,4 +36,138 @@ describe('storeCategorySchema', () => {
|
|
|
29
36
|
expect(storeCategorySchema.safeParse({ label: 'Solo', href: '/categorias/x', children: [{}] }).success).toBe(false)
|
|
30
37
|
expect(storeCategorySchema.safeParse({}).success).toBe(false)
|
|
31
38
|
})
|
|
32
|
-
})
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
describe('productCardPropsSchema', () => {
|
|
42
|
+
const canonicalItem = {
|
|
43
|
+
id: 'sku-1',
|
|
44
|
+
title: 'Llave de impacto',
|
|
45
|
+
price: { amount: '129.50', currencyCode: 'EUR' },
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
it('accepts a single canonical Money item with the bounded presentation options', () => {
|
|
49
|
+
expect(productCardPropsSchema.safeParse({ item: canonicalItem }).success).toBe(true)
|
|
50
|
+
expect(
|
|
51
|
+
productCardPropsSchema.safeParse({
|
|
52
|
+
item: canonicalItem,
|
|
53
|
+
presentation: 'classic',
|
|
54
|
+
wishlisted: true,
|
|
55
|
+
}).success,
|
|
56
|
+
).toBe(true)
|
|
57
|
+
expect(
|
|
58
|
+
productCardPropsSchema.safeParse({ item: canonicalItem, presentation: 'rich' }).success,
|
|
59
|
+
).toBe(true)
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
it('rejects a collection in place of the single item', () => {
|
|
63
|
+
expect(productCardPropsSchema.safeParse({ items: [canonicalItem] }).success).toBe(false)
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
it('rejects legacy numeric-price items', () => {
|
|
67
|
+
expect(
|
|
68
|
+
productCardPropsSchema.safeParse({ item: { id: 'p1', title: 'Taladro', price: 49.99, image: '/x.svg' } })
|
|
69
|
+
.success,
|
|
70
|
+
).toBe(false)
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
it('rejects unknown presentation options', () => {
|
|
74
|
+
expect(
|
|
75
|
+
productCardPropsSchema.safeParse({ item: canonicalItem, presentation: 'compact' }).success,
|
|
76
|
+
).toBe(false)
|
|
77
|
+
})
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
describe('productGridPropsSchema', () => {
|
|
81
|
+
const canonicalItem = {
|
|
82
|
+
id: 'sku-1',
|
|
83
|
+
title: 'Llave de impacto',
|
|
84
|
+
price: { amount: '129.50', currencyCode: 'EUR' },
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
it('accepts a collection with the listing state options', () => {
|
|
88
|
+
expect(productGridPropsSchema.safeParse({ items: [canonicalItem] }).success).toBe(true)
|
|
89
|
+
expect(
|
|
90
|
+
productGridPropsSchema.safeParse({
|
|
91
|
+
items: [canonicalItem],
|
|
92
|
+
presentation: 'minimal',
|
|
93
|
+
heading: 'Destacados',
|
|
94
|
+
maxItems: 6,
|
|
95
|
+
wishlistedIds: ['sku-1'],
|
|
96
|
+
loading: false,
|
|
97
|
+
skeletonCount: 6,
|
|
98
|
+
emptyLabel: 'Sin productos',
|
|
99
|
+
}).success,
|
|
100
|
+
).toBe(true)
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
it('declares only serializable keys — no callbacks or label strings', () => {
|
|
104
|
+
const runtimeOnlyKeys = [
|
|
105
|
+
'onAddToCart',
|
|
106
|
+
'onProductClick',
|
|
107
|
+
'onToggleWishlist',
|
|
108
|
+
'onViewSimilar',
|
|
109
|
+
'addToCartLabel',
|
|
110
|
+
'unavailablePriceLabel',
|
|
111
|
+
'addToWishlistLabel',
|
|
112
|
+
'removeFromWishlistLabel',
|
|
113
|
+
'viewSimilarLabel',
|
|
114
|
+
]
|
|
115
|
+
const schemaOwnedLabelKeys = new Set(['emptyLabel'])
|
|
116
|
+
const schemas: Array<{ name: string; schema: z.AnyZodObject }> = [
|
|
117
|
+
{ name: 'productCardPropsSchema', schema: productCardPropsSchema },
|
|
118
|
+
{ name: 'productGridPropsSchema', schema: productGridPropsSchema },
|
|
119
|
+
]
|
|
120
|
+
|
|
121
|
+
for (const { name, schema } of schemas) {
|
|
122
|
+
const declaredKeys = Object.keys(schema.shape)
|
|
123
|
+
expect(declaredKeys.length, name).toBeGreaterThan(0)
|
|
124
|
+
for (const key of declaredKeys) {
|
|
125
|
+
expect(runtimeOnlyKeys, `${name}.${key}`).not.toContain(key)
|
|
126
|
+
expect(key, `${name}.${key}`).not.toMatch(/^on[A-Z]/)
|
|
127
|
+
if (!schemaOwnedLabelKeys.has(key)) {
|
|
128
|
+
expect(key, `${name}.${key}`).not.toMatch(/Label$/)
|
|
129
|
+
}
|
|
130
|
+
expect(schema.shape[key], `${name}.${key}`).not.toBeInstanceOf(z.ZodFunction)
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
it('rejects non-positive maxItems and skeletonCount', () => {
|
|
136
|
+
expect(productGridPropsSchema.safeParse({ items: [], maxItems: 0 }).success).toBe(false)
|
|
137
|
+
expect(productGridPropsSchema.safeParse({ items: [], skeletonCount: 0 }).success).toBe(false)
|
|
138
|
+
})
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
describe('category cell and listing contracts', () => {
|
|
142
|
+
const category = { id: 'cat-tools', name: 'Herramientas' }
|
|
143
|
+
|
|
144
|
+
it('keeps category cell and listing props JSON-serializable and callback-free', () => {
|
|
145
|
+
const samples: Array<{ name: string; schema: z.AnyZodObject; props: unknown }> = [
|
|
146
|
+
{
|
|
147
|
+
name: 'categoryCardPropsSchema',
|
|
148
|
+
schema: categoryCardPropsSchema,
|
|
149
|
+
props: { category, presentation: 'grid', browseLabel: 'Explorar' },
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
name: 'categoryGridPropsSchema',
|
|
153
|
+
schema: categoryGridPropsSchema,
|
|
154
|
+
props: { categories: [category], presentation: 'grid', title: 'Categorías', maxItems: 4 },
|
|
155
|
+
},
|
|
156
|
+
]
|
|
157
|
+
|
|
158
|
+
for (const { name, schema, props } of samples) {
|
|
159
|
+
const parsed = schema.parse(props)
|
|
160
|
+
expect(JSON.parse(JSON.stringify(parsed)), name).toEqual(parsed)
|
|
161
|
+
for (const key of Object.keys(schema.shape)) {
|
|
162
|
+
expect(key, `${name}.${key}`).not.toMatch(/^on[A-Z]/)
|
|
163
|
+
expect(schema.shape[key], `${name}.${key}`).not.toBeInstanceOf(z.ZodFunction)
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
it('keeps display labels serializable strings and rejects a collection in the cell contract', () => {
|
|
169
|
+
expect(categoryCardPropsSchema.safeParse({ category, browseLabel: () => 'Explorar' }).success).toBe(false)
|
|
170
|
+
expect(categoryGridPropsSchema.safeParse({ categories: [], emptyLabel: () => 'Vacío' }).success).toBe(false)
|
|
171
|
+
expect(categoryCardPropsSchema.safeParse({ categories: [category] }).success).toBe(false)
|
|
172
|
+
})
|
|
173
|
+
})
|