@thorprovider/blocks 0.1.2 → 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.
Files changed (111) hide show
  1. package/CHANGELOG.md +107 -0
  2. package/README.md +108 -23
  3. package/docs/standards/data-contracts.md +13 -2
  4. package/package.json +26 -10
  5. package/scripts/fix-esm-extensions.mjs +40 -0
  6. package/src/blocks/e-commerce/navigation/back-to-top.test.tsx +27 -0
  7. package/src/blocks/e-commerce/navigation/back-to-top.tsx +71 -0
  8. package/src/blocks/e-commerce/navigation/breadcrumb.test.tsx +174 -0
  9. package/src/blocks/e-commerce/navigation/breadcrumb.tsx +130 -0
  10. package/src/blocks/e-commerce/navigation/mobile-bottom-nav.test.tsx +74 -0
  11. package/src/blocks/e-commerce/navigation/mobile-bottom-nav.tsx +78 -0
  12. package/src/blocks/e-commerce/navigation/skip-to-content.test.tsx +35 -0
  13. package/src/blocks/e-commerce/navigation/skip-to-content.tsx +37 -0
  14. package/src/blocks/e-commerce/product-categories/category-card-grid.test.tsx +122 -0
  15. package/src/blocks/e-commerce/product-categories/category-card.test.tsx +68 -0
  16. package/src/blocks/e-commerce/product-categories/category-card.tsx +172 -0
  17. package/src/blocks/e-commerce/product-categories/category-grid.test.tsx +67 -0
  18. package/src/blocks/e-commerce/product-categories/category-shared.tsx +109 -0
  19. package/src/blocks/e-commerce/product-categories/product-category-grid.tsx +170 -0
  20. package/src/blocks/e-commerce/product-categories/product-category.test.tsx +266 -0
  21. package/src/blocks/e-commerce/product-categories/product-category.tsx +462 -0
  22. package/src/blocks/e-commerce/product-list/product-card.test.tsx +263 -0
  23. package/src/blocks/e-commerce/product-list/product-card.tsx +424 -0
  24. package/src/blocks/e-commerce/product-list/product-grid.test.tsx +162 -0
  25. package/src/blocks/e-commerce/product-list/product-grid.tsx +114 -0
  26. package/src/blocks/e-commerce/product-list/product-showcase-1.test.tsx +78 -0
  27. package/src/blocks/e-commerce/product-list/product-showcase-1.tsx +277 -0
  28. package/src/blocks/e-commerce/product-overview/product-overview-gallery.tsx +71 -0
  29. package/src/blocks/e-commerce/product-overview/{product-overview-5-lightbox-tab.tsx → product-overview-lightbox-tab.tsx} +35 -23
  30. package/src/blocks/e-commerce/product-overview/{product-overview-5-reviews-tab.tsx → product-overview-reviews-tab.tsx} +40 -17
  31. package/src/blocks/e-commerce/product-overview/{product-overview-5-tabs.tsx → product-overview-tabs.tsx} +8 -7
  32. package/src/blocks/e-commerce/product-overview/product-overview.test.tsx +283 -0
  33. package/src/blocks/e-commerce/product-overview/product-overview.tsx +1186 -0
  34. package/src/blocks/e-commerce/reviews/review-rating-2.tsx +5 -6
  35. package/src/blocks/e-commerce/shopping-carts/shopping-cart.test.tsx +399 -0
  36. package/src/blocks/e-commerce/shopping-carts/{shopping-cart-4.tsx → shopping-cart.tsx} +204 -109
  37. package/src/blocks/e-commerce/storefront-hero/storefront-hero.test.tsx +412 -0
  38. package/src/blocks/e-commerce/storefront-hero/storefront-hero.tsx +1283 -0
  39. package/src/blocks/marketing/features/feature.test.tsx +153 -0
  40. package/src/blocks/marketing/features/feature.tsx +195 -0
  41. package/src/blocks/marketing/navbars/navbar-4-account-menu.test.tsx +56 -0
  42. package/src/blocks/marketing/navbars/navbar-4-account-menu.tsx +30 -14
  43. package/src/blocks/marketing/navbars/navbar-4-cart-sheet.test.tsx +150 -0
  44. package/src/blocks/marketing/navbars/navbar-4-cart-sheet.tsx +102 -9
  45. package/src/blocks/marketing/navbars/navbar-4.test.tsx +154 -2
  46. package/src/blocks/marketing/navbars/navbar-4.tsx +152 -32
  47. package/src/index.ts +93 -46
  48. package/src/lib/contracts-curated.test.ts +42 -24
  49. package/src/lib/contracts.test.ts +143 -2
  50. package/src/lib/contracts.ts +395 -13
  51. package/src/ui/button-group.tsx +1 -1
  52. package/src/ui/carousel.tsx +1 -1
  53. package/src/ui/dialog.tsx +1 -1
  54. package/src/ui/field.tsx +2 -2
  55. package/src/ui/input-group.tsx +3 -3
  56. package/src/ui/pagination.tsx +1 -1
  57. package/src/ui/sidebar.tsx +7 -7
  58. package/src/ui/toggle-group.tsx +1 -1
  59. package/tsconfig.build.json +12 -0
  60. package/tsup.config.ts +11 -7
  61. package/.turbo/turbo-build.log +0 -24
  62. package/.turbo/turbo-test.log +0 -37
  63. package/.turbo/turbo-type-check.log +0 -0
  64. package/dist/.tsbuildinfo +0 -1
  65. package/dist/contracts.d.mts +0 -1584
  66. package/dist/contracts.d.ts +0 -1584
  67. package/dist/contracts.js +0 -390
  68. package/dist/contracts.js.map +0 -1
  69. package/dist/contracts.mjs +0 -334
  70. package/dist/contracts.mjs.map +0 -1
  71. package/dist/index.d.mts +0 -2786
  72. package/dist/index.d.ts +0 -2786
  73. package/dist/index.js +0 -17139
  74. package/dist/index.js.map +0 -1
  75. package/dist/index.mjs +0 -17059
  76. package/dist/index.mjs.map +0 -1
  77. package/src/blocks/e-commerce/product-categories/avatar-group.tsx +0 -86
  78. package/src/blocks/e-commerce/product-categories/product-category-1.tsx +0 -126
  79. package/src/blocks/e-commerce/product-categories/product-category-2.test.tsx +0 -51
  80. package/src/blocks/e-commerce/product-categories/product-category-2.tsx +0 -120
  81. package/src/blocks/e-commerce/product-categories/product-category-4.tsx +0 -129
  82. package/src/blocks/e-commerce/product-categories/product-category-5.tsx +0 -218
  83. package/src/blocks/e-commerce/product-list/product-card-1.tsx +0 -131
  84. package/src/blocks/e-commerce/product-list/product-card-2.tsx +0 -71
  85. package/src/blocks/e-commerce/product-list/product-card-3-reaction-toggle.tsx +0 -166
  86. package/src/blocks/e-commerce/product-list/product-card-3.tsx +0 -215
  87. package/src/blocks/e-commerce/product-list/product-card-5-checkbox-check.tsx +0 -98
  88. package/src/blocks/e-commerce/product-list/product-card-5-reaction-toggle.tsx +0 -171
  89. package/src/blocks/e-commerce/product-list/product-card-5.tsx +0 -367
  90. package/src/blocks/e-commerce/product-overview/product-overview-1.test.tsx +0 -182
  91. package/src/blocks/e-commerce/product-overview/product-overview-1.tsx +0 -370
  92. package/src/blocks/e-commerce/product-overview/product-overview-2.tsx +0 -272
  93. package/src/blocks/e-commerce/product-overview/product-overview-5-gallery.tsx +0 -59
  94. package/src/blocks/e-commerce/product-overview/product-overview-5.tsx +0 -274
  95. package/src/blocks/e-commerce/shopping-carts/shopping-cart-1-spinning-counter.tsx +0 -142
  96. package/src/blocks/e-commerce/shopping-carts/shopping-cart-1.tsx +0 -291
  97. package/src/blocks/e-commerce/shopping-carts/shopping-cart-2-item-row.tsx +0 -110
  98. package/src/blocks/e-commerce/shopping-carts/shopping-cart-2-recommendations.tsx +0 -51
  99. package/src/blocks/e-commerce/shopping-carts/shopping-cart-2-spinning-counter.tsx +0 -146
  100. package/src/blocks/e-commerce/shopping-carts/shopping-cart-2.tsx +0 -295
  101. package/src/blocks/e-commerce/shopping-carts/shopping-cart-3-spinning-counter.tsx +0 -142
  102. package/src/blocks/e-commerce/shopping-carts/shopping-cart-3.tsx +0 -342
  103. package/src/blocks/e-commerce/shopping-carts/shopping-cart-4-spinning-counter.tsx +0 -146
  104. package/src/blocks/e-commerce/shopping-carts/shopping-cart-4.test.tsx +0 -137
  105. package/src/blocks/e-commerce/storefront-hero/storefront-hero-1.test.tsx +0 -65
  106. package/src/blocks/e-commerce/storefront-hero/storefront-hero-1.tsx +0 -263
  107. package/src/blocks/e-commerce/storefront-hero/storefront-hero-2.tsx +0 -283
  108. package/src/blocks/e-commerce/storefront-hero/storefront-hero-5.tsx +0 -318
  109. package/src/blocks/e-commerce/storefront-hero/storefront-hero-6.tsx +0 -205
  110. package/src/blocks/marketing/features/feature-1.tsx +0 -97
  111. 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 Feature1 } from './blocks/marketing/features/feature-1'
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 { Feature1Props, Feature1Item } from './blocks/marketing/features/feature-1'
35
- export type { Feature2Props, Feature2Item } from './blocks/marketing/features/feature-2'
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,57 @@ export type {
52
54
  FooterSection4SocialIcon,
53
55
  FooterSection4SocialLink,
54
56
  } from './blocks/marketing/footers/footer-section-4'
55
- export { default as ProductCard1 } from './blocks/e-commerce/product-list/product-card-1'
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 ProductOverview1 } from './blocks/e-commerce/product-overview/product-overview-1'
60
- export type {
61
- ProductOverview1Props,
62
- ProductOverview1Rating,
57
+ export { ProductCard } from './blocks/e-commerce/product-list/product-card'
58
+ export type {
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'
65
+ export type { ProductShowcase1Props } from './blocks/e-commerce/product-list/product-showcase-1'
66
+ export { default as ProductOverview } from './blocks/e-commerce/product-overview/product-overview'
67
+ export type {
68
+ ProductOverviewProps,
69
+ ProductOverviewRating,
70
+ ProductOverviewPresentation,
71
+ ProductOverviewReview,
63
72
  ProductDetailProduct,
64
73
  ProductDetailVariant,
65
- } from './blocks/e-commerce/product-overview/product-overview-1'
66
- export { default as ProductOverview2 } from './blocks/e-commerce/product-overview/product-overview-2'
67
- export { default as ProductOverview5 } from './blocks/e-commerce/product-overview/product-overview-5'
68
- export type { ProductOverview2Props, ProductOverview2Product } from './blocks/e-commerce/product-overview/product-overview-2'
69
- export type { ProductOverview5Props, ProductOverview5Product, ProductOverview5Review } from './blocks/e-commerce/product-overview/product-overview-5'
74
+ } from './blocks/e-commerce/product-overview/product-overview'
70
75
  export { default as ProductQuickView } from './blocks/e-commerce/product-overview/product-quick-view'
71
76
  export { default as ReviewRating1 } from './blocks/e-commerce/reviews/review-rating-1'
72
77
  export type { ReviewRating1Review } from './blocks/e-commerce/reviews/review-rating-1'
73
78
  export { default as ReviewRating2 } from './blocks/e-commerce/reviews/review-rating-2'
74
- export { default as ProductCategory1 } from './blocks/e-commerce/product-categories/product-category-1'
75
- export type {
76
- ProductCategory1Category,
77
- ProductCategory1CategoryIcon,
78
- ProductCategory1Props,
79
- } from './blocks/e-commerce/product-categories/product-category-1'
80
- export { default as ProductCategory2 } from './blocks/e-commerce/product-categories/product-category-2'
81
- export type { ProductCategory2Category, ProductCategory2Props } from './blocks/e-commerce/product-categories/product-category-2'
82
- export { default as ProductCategory4 } from './blocks/e-commerce/product-categories/product-category-4'
83
- export { default as ProductCategory5 } from './blocks/e-commerce/product-categories/product-category-5'
79
+ export { default as ProductCategory } from './blocks/e-commerce/product-categories/product-category'
80
+ export type {
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'
84
94
  export { MegaMenu, megaMenuPropsSchema } from './blocks/e-commerce/navigation/mega-menu'
85
95
  export type { MegaMenuProps } from './blocks/e-commerce/navigation/mega-menu'
86
96
  export { MobileCategoryDrawer, mobileCategoryDrawerPropsSchema } from './blocks/e-commerce/navigation/mobile-category-drawer'
87
97
  export type { MobileCategoryDrawerProps } from './blocks/e-commerce/navigation/mobile-category-drawer'
88
98
  export { CategoryIcon, categoryIconNameSchema, categoryIconPropsSchema } from './blocks/e-commerce/navigation/category-icon'
89
99
  export type { CategoryIconName, CategoryIconProps } from './blocks/e-commerce/navigation/category-icon'
100
+ export { Breadcrumb, breadcrumbItemSchema, breadcrumbPropsSchema } from './blocks/e-commerce/navigation/breadcrumb'
101
+ export type { BreadcrumbItemData, BreadcrumbProps } from './blocks/e-commerce/navigation/breadcrumb'
102
+ export { SkipToContent, skipToContentPropsSchema } from './blocks/e-commerce/navigation/skip-to-content'
103
+ export type { SkipToContentProps } from './blocks/e-commerce/navigation/skip-to-content'
104
+ export { BackToTop, backToTopPropsSchema, resolveScrollBehavior } from './blocks/e-commerce/navigation/back-to-top'
105
+ export type { BackToTopProps } from './blocks/e-commerce/navigation/back-to-top'
106
+ export { MobileBottomNav, mobileBottomNavItemSchema, mobileBottomNavPropsSchema } from './blocks/e-commerce/navigation/mobile-bottom-nav'
107
+ export type { MobileBottomNavItem, MobileBottomNavProps } from './blocks/e-commerce/navigation/mobile-bottom-nav'
90
108
  export { menuItemSchema, collectionItemSchema } from './lib/types/menu'
91
109
  export type { MenuItem, CollectionItem } from './lib/types/menu'
92
110
  export { default as CategoryFilter1 } from './blocks/e-commerce/category-filters/category-filter-1'
@@ -114,13 +132,13 @@ export type {
114
132
  } from './blocks/e-commerce/category-filters/category-filter-2'
115
133
  export { default as CategoryFilter4 } from './blocks/e-commerce/category-filters/category-filter-4'
116
134
  export type { CategoryFilter4Props, CategoryFilter4Product, CategoryFilter4Facets } from './blocks/e-commerce/category-filters/category-filter-4'
117
- export { default as ShoppingCart1 } from './blocks/e-commerce/shopping-carts/shopping-cart-1'
118
- export { default as ShoppingCart2 } from './blocks/e-commerce/shopping-carts/shopping-cart-2'
119
- export { default as ShoppingCart4 } from './blocks/e-commerce/shopping-carts/shopping-cart-4'
120
- export { default as ShoppingCart3 } from './blocks/e-commerce/shopping-carts/shopping-cart-3'
121
- export type { ShoppingCart3Props, ShoppingCart3CartItem } from './blocks/e-commerce/shopping-carts/shopping-cart-3'
122
- export type { ShoppingCart2Props, ShoppingCart2Item } from './blocks/e-commerce/shopping-carts/shopping-cart-2'
123
- export type { ShoppingCart4Props, ShoppingCart4Item } from './blocks/e-commerce/shopping-carts/shopping-cart-4'
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'
124
142
  export { default as StoreContainer1 } from './blocks/e-commerce/store-containers/store-container-1'
125
143
  export type { StoreContainer1Props } from './blocks/e-commerce/store-containers/store-container-1'
126
144
  export { default as CheckoutForm1 } from './blocks/e-commerce/checkout-forms/checkout-form-1'
@@ -167,14 +185,18 @@ export type {
167
185
  export type { CheckoutFieldError, CheckoutFieldErrors } from './blocks/e-commerce/checkout-forms/checkout-form-types'
168
186
  export { default as OrderHistory1 } from './blocks/e-commerce/order-history/order-history-1'
169
187
  export { default as OrderHistory2 } from './blocks/e-commerce/order-history/order-history-2'
170
- export { default as StorefrontHero1 } from './blocks/e-commerce/storefront-hero/storefront-hero-1'
188
+ export { default as StorefrontHero } from './blocks/e-commerce/storefront-hero/storefront-hero'
171
189
  export type {
172
190
  FeaturedProduct,
173
- StorefrontHero1Props,
174
- } from './blocks/e-commerce/storefront-hero/storefront-hero-1'
175
- export { default as StorefrontHero2 } from './blocks/e-commerce/storefront-hero/storefront-hero-2'
176
- export { default as StorefrontHero5 } from './blocks/e-commerce/storefront-hero/storefront-hero-5'
177
- export { default as StorefrontHero6 } from './blocks/e-commerce/storefront-hero/storefront-hero-6'
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'
178
200
  export { default as AppShell1 } from './blocks/application/app-shells/app-shell-1'
179
201
  export { default as LoginPage1 } from './blocks/application/login-forms/login-page-1'
180
202
  export { default as LoginPage2 } from './blocks/application/login-forms/login-page-2'
@@ -201,11 +223,18 @@ export {
201
223
  cartTotalsSchema,
202
224
  productDetailProductSchema,
203
225
  productDetailVariantSchema,
204
- productOverview1PropsSchema,
226
+ productOverviewPropsSchema,
227
+ storefrontHeroPropsSchema,
228
+ storefrontHeroProductSchema,
205
229
  buttonPropsSchema,
206
230
  storeContainer1PropsSchema,
207
- productCard1PropsSchema,
208
- shoppingCart4PropsSchema,
231
+ productCardPropsSchema,
232
+ productGridPropsSchema,
233
+ productPresentationSchema,
234
+ productShowcase1PropsSchema,
235
+ shoppingCartPropsSchema,
236
+ shoppingCartItemSchema,
237
+ shoppingCartSuggestionSchema,
209
238
  navbar4PropsSchema,
210
239
  paymentMethodSchema,
211
240
  cardProductSchema,
@@ -216,16 +245,27 @@ export {
216
245
  navCartItemSchema,
217
246
  storeUserSchema,
218
247
  accountMenuItemSchema,
248
+ featurePropsSchema,
249
+ featureCardsSpecSchema,
250
+ featureSplitSpecSchema,
251
+ productCategoryItemSchema,
252
+ productCategoryPropsSchema,
219
253
  } from './lib/contracts'
220
254
  export type {
221
255
  ProductDetailProductSpec,
222
256
  ProductDetailVariantSpec,
223
- ProductOverview1SpecProps,
257
+ StorefrontHeroProduct,
258
+ StorefrontHeroSpecProps,
224
259
  CartTotals,
225
260
  ButtonSpecProps,
226
261
  StoreContainer1SpecProps,
227
- ProductCard1SpecProps,
228
- ShoppingCart4SpecProps,
262
+ ProductCardSpecProps,
263
+ ProductGridSpecProps,
264
+ ProductPresentation,
265
+ ProductShowcase1SpecProps,
266
+ ShoppingCartItem,
267
+ ShoppingCartSuggestion,
268
+ ShoppingCartSpecProps,
229
269
  Navbar4SpecProps,
230
270
  CardProduct,
231
271
  CartItem,
@@ -235,4 +275,11 @@ export type {
235
275
  NavCartItem,
236
276
  StoreUser,
237
277
  AccountMenuItem,
278
+ FeatureSpecProps,
279
+ FeatureCardsSpecProps,
280
+ FeatureSplitSpecProps,
281
+ ProductCategoryItem,
282
+ ProductCategorySpecProps,
283
+ CategoryCardSpecProps,
284
+ CategoryGridSpecProps,
238
285
  } from './lib/contracts'
@@ -11,8 +11,8 @@ import {
11
11
  moneySchema,
12
12
  navCartItemSchema,
13
13
  navbar4PropsSchema,
14
- productOverview1PropsSchema,
15
- shoppingCart4PropsSchema,
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('productOverview1PropsSchema', () => {
58
+ describe('productOverviewPropsSchema', () => {
59
59
  it('accepts a product whose variant price is omitted or null (unavailable)', () => {
60
- expect(productOverview1PropsSchema.safeParse({ product }).success).toBe(true)
60
+ expect(productOverviewPropsSchema.safeParse({ product }).success).toBe(true)
61
61
  })
62
62
 
63
63
  it('accepts an explicit controlled selected variant id', () => {
64
- expect(productOverview1PropsSchema.safeParse({ product, selectedVariantId: 'v1' }).success).toBe(true)
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(productOverview1PropsSchema.safeParse({ product: bad }).success).toBe(false)
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 { storeCategorySchema } from './contracts'
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
+ })