@akinon/pz-theme 2.0.37-rc.0 → 2.0.38-beta.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 (78) hide show
  1. package/CHANGELOG.md +15 -31
  2. package/package.json +4 -4
  3. package/src/__tests__/__snapshots__/facet-filters-block.test.tsx.snap +11 -0
  4. package/src/__tests__/custom-css.test.ts +181 -0
  5. package/src/__tests__/designer-chrome-clipping.test.tsx +497 -0
  6. package/src/__tests__/facet-filters-block.test.tsx +707 -0
  7. package/src/__tests__/item-template.test.tsx +184 -0
  8. package/src/__tests__/listing-controls.test.tsx +961 -0
  9. package/src/__tests__/part-style-scope.test.tsx +229 -0
  10. package/src/__tests__/part-styles.test.ts +311 -0
  11. package/src/__tests__/registry-parity.test.ts +42 -0
  12. package/src/__tests__/row-visibility.test.ts +672 -0
  13. package/src/__tests__/sort-select-page-cursor.test.tsx +336 -0
  14. package/src/__tests__/theme-block-html-id.test.tsx +167 -0
  15. package/src/blocks/__tests__/__snapshots__/product-gallery-block.test.tsx.snap +15 -0
  16. package/src/blocks/__tests__/accordion-block-state.test.tsx +201 -0
  17. package/src/blocks/__tests__/facet-filters-block.test.tsx +190 -0
  18. package/src/blocks/__tests__/listing-context-card-model.test.tsx +230 -0
  19. package/src/blocks/__tests__/listing-lookup-hardening.test.ts +80 -0
  20. package/src/blocks/__tests__/product-gallery-block.test.tsx +974 -0
  21. package/src/blocks/__tests__/product-grid-composition.test.tsx +570 -0
  22. package/src/blocks/__tests__/product-grid-lookups.test.tsx +230 -0
  23. package/src/blocks/__tests__/product-price-block.test.tsx +248 -0
  24. package/src/blocks/__tests__/product-recommendations-block.test.tsx +702 -0
  25. package/src/blocks/__tests__/share-block.test.tsx +392 -0
  26. package/src/blocks/__tests__/store-locator-block.test.tsx +691 -0
  27. package/src/blocks/__tests__/variant-selector-block.test.tsx +817 -0
  28. package/src/blocks/__tests__/variant-selector-golden.json +20 -0
  29. package/src/blocks/account-menu-block.tsx +264 -0
  30. package/src/blocks/add-to-cart-block.tsx +417 -0
  31. package/src/blocks/address-list-block.tsx +348 -0
  32. package/src/blocks/applied-filters-block.tsx +288 -0
  33. package/src/blocks/auth-form-block.tsx +295 -0
  34. package/src/blocks/basket-items-block.tsx +457 -0
  35. package/src/blocks/basket-summary-block.tsx +404 -0
  36. package/src/blocks/block-renderer-registry.tsx +94 -0
  37. package/src/blocks/change-password-form-block.tsx +240 -0
  38. package/src/blocks/checkout-steps-block.tsx +288 -0
  39. package/src/blocks/checkout-summary-block.tsx +449 -0
  40. package/src/blocks/countdown-block.tsx +182 -0
  41. package/src/blocks/coupon-list-block.tsx +292 -0
  42. package/src/blocks/facet-filters-block.tsx +1686 -0
  43. package/src/blocks/faq-accordion-block.tsx +358 -0
  44. package/src/blocks/favorite-button-block.tsx +252 -0
  45. package/src/blocks/favourites-grid-block.tsx +510 -0
  46. package/src/blocks/forgot-password-block.tsx +155 -0
  47. package/src/blocks/item-template-utils.ts +131 -0
  48. package/src/blocks/item-template.tsx +164 -0
  49. package/src/blocks/listing-breadcrumb-block.tsx +175 -0
  50. package/src/blocks/listing-context.ts +850 -0
  51. package/src/blocks/listing-pagination-block.tsx +923 -0
  52. package/src/blocks/listing-result-count-block.tsx +126 -0
  53. package/src/blocks/listing-title-block.tsx +115 -0
  54. package/src/blocks/oauth-buttons-block.tsx +205 -0
  55. package/src/blocks/order-detail-block.tsx +474 -0
  56. package/src/blocks/order-list-block.tsx +274 -0
  57. package/src/blocks/order-success-block.tsx +641 -0
  58. package/src/blocks/order-tracking-block.tsx +533 -0
  59. package/src/blocks/product-attributes-block.tsx +307 -0
  60. package/src/blocks/product-block-utils.tsx +639 -0
  61. package/src/blocks/product-breadcrumb-block.tsx +215 -0
  62. package/src/blocks/product-description-block.tsx +169 -0
  63. package/src/blocks/product-gallery-block.tsx +1523 -0
  64. package/src/blocks/product-grid-block.tsx +966 -0
  65. package/src/blocks/product-price-block.tsx +373 -0
  66. package/src/blocks/product-recommendations-block.tsx +1433 -0
  67. package/src/blocks/product-title-block.tsx +129 -0
  68. package/src/blocks/profile-form-block.tsx +297 -0
  69. package/src/blocks/recently-viewed-store.ts +201 -0
  70. package/src/blocks/register-form-block.tsx +362 -0
  71. package/src/blocks/share-block.tsx +667 -0
  72. package/src/blocks/size-guide-block.tsx +313 -0
  73. package/src/blocks/sort-select-block.tsx +636 -0
  74. package/src/blocks/stock-status-block.tsx +168 -0
  75. package/src/blocks/store-locator-block.tsx +1184 -0
  76. package/src/blocks/variant-selector-block.tsx +1110 -0
  77. package/src/sections/__tests__/tablet-raw-bucket-readers.test.tsx +186 -0
  78. package/src/utils/__tests__/part-ownership-guard.test.tsx +29 -4
@@ -0,0 +1,168 @@
1
+ 'use client';
2
+
3
+ import React from 'react';
4
+ import { getResponsiveValue, partAttrs } from '../utils';
5
+ import { BlockRendererProps } from './block-renderer-registry';
6
+ import {
7
+ DesignerPlaceholder,
8
+ isProductInStock,
9
+ pickProductField,
10
+ readToggle,
11
+ useThemeProduct
12
+ } from './product-block-utils';
13
+
14
+ const ALLOWED_INDICATOR_SHAPES = ['dot', 'square'];
15
+ const ALLOWED_INDICATOR_POSITIONS = ['before', 'after'];
16
+
17
+ /**
18
+ * Functional PDP block: availability label derived from the commerce stock
19
+ * fields (in_stock, falling back to the stock count). The editor owns the
20
+ * wording, the low-stock threshold, the indicator and — through named parts —
21
+ * the styling of the pill in each status.
22
+ * Properties: inStockLabel, outOfStockLabel, lowStockLabel, lowStockThreshold,
23
+ * showIndicator, indicatorShape, indicatorSize, indicatorPosition,
24
+ * inStockColor, outOfStockColor, lowStockColor, showCount, countTemplate.
25
+ */
26
+ const StockStatusBlock = ({
27
+ block,
28
+ currentBreakpoint = 'desktop',
29
+ isDesigner = false
30
+ }: BlockRendererProps) => {
31
+ const product = useThemeProduct();
32
+
33
+ const prop = (key: string, fallback: string) =>
34
+ String(
35
+ getResponsiveValue(block.properties?.[key], currentBreakpoint, fallback)
36
+ );
37
+
38
+ const inStockLabel = prop('inStockLabel', 'In Stock');
39
+ const outOfStockLabel = prop('outOfStockLabel', 'Out of Stock');
40
+ const lowStockLabel = prop('lowStockLabel', 'Low stock');
41
+ const inStockColor = prop('inStockColor', '');
42
+ const outOfStockColor = prop('outOfStockColor', '');
43
+ const lowStockColor = prop('lowStockColor', '');
44
+ const countTemplate = prop('countTemplate', '{count} left');
45
+ const showIndicator = readToggle(
46
+ block.properties?.showIndicator,
47
+ currentBreakpoint,
48
+ true
49
+ );
50
+ const showCount = readToggle(
51
+ block.properties?.showCount,
52
+ currentBreakpoint,
53
+ false
54
+ );
55
+ const indicatorShapeValue = prop('indicatorShape', 'dot');
56
+ const indicatorShape = ALLOWED_INDICATOR_SHAPES.includes(indicatorShapeValue)
57
+ ? indicatorShapeValue
58
+ : 'dot';
59
+ const indicatorPositionValue = prop('indicatorPosition', 'before');
60
+ const indicatorPosition = ALLOWED_INDICATOR_POSITIONS.includes(
61
+ indicatorPositionValue
62
+ )
63
+ ? indicatorPositionValue
64
+ : 'before';
65
+ const indicatorSizeValue = Number(
66
+ getResponsiveValue(block.properties?.indicatorSize, currentBreakpoint, 8)
67
+ );
68
+ const indicatorSize =
69
+ Number.isFinite(indicatorSizeValue) && indicatorSizeValue > 0
70
+ ? indicatorSizeValue
71
+ : 8;
72
+ const lowStockThresholdValue = Number(
73
+ getResponsiveValue(
74
+ block.properties?.lowStockThreshold,
75
+ currentBreakpoint,
76
+ 0
77
+ )
78
+ );
79
+ const lowStockThreshold =
80
+ Number.isFinite(lowStockThresholdValue) && lowStockThresholdValue > 0
81
+ ? lowStockThresholdValue
82
+ : 0;
83
+
84
+ const hasProduct = Boolean(product) && pickProductField(product, 'pk') !== undefined;
85
+
86
+ if (!hasProduct) {
87
+ return isDesigner ? (
88
+ <DesignerPlaceholder
89
+ label="Stock status (no product)"
90
+ attribute="data-stock-status"
91
+ />
92
+ ) : null;
93
+ }
94
+
95
+ const inStock = isProductInStock(product);
96
+ const stockCount = Number(pickProductField(product, 'stock'));
97
+ const hasCount = Number.isFinite(stockCount) && stockCount > 0;
98
+ // A threshold of 0 turns the third status off entirely, which is what keeps
99
+ // every existing composition rendering the two labels it renders today.
100
+ const isLowStock =
101
+ inStock && lowStockThreshold > 0 && hasCount && stockCount <= lowStockThreshold;
102
+
103
+ const label = !inStock
104
+ ? outOfStockLabel
105
+ : isLowStock
106
+ ? lowStockLabel
107
+ : inStockLabel;
108
+ const color = !inStock
109
+ ? outOfStockColor
110
+ : isLowStock
111
+ ? lowStockColor || inStockColor
112
+ : inStockColor;
113
+ const state = !inStock ? 'out-of-stock' : isLowStock ? 'low-stock' : 'in-stock';
114
+
115
+ if (!label) {
116
+ return isDesigner ? (
117
+ <DesignerPlaceholder
118
+ label="Stock status (no label)"
119
+ attribute="data-stock-status"
120
+ />
121
+ ) : null;
122
+ }
123
+
124
+ const indicatorNode = showIndicator ? (
125
+ <span
126
+ key="indicator"
127
+ data-stock-indicator=""
128
+ {...partAttrs('indicator', { empty: !inStock })}
129
+ aria-hidden="true"
130
+ style={{
131
+ width: `${indicatorSize}px`,
132
+ height: `${indicatorSize}px`,
133
+ borderRadius: indicatorShape === 'square' ? '2px' : '50%',
134
+ flex: '0 0 auto',
135
+ background: inStock
136
+ ? color || 'var(--stock-in-color, #16a34a)'
137
+ : color || 'var(--stock-out-color, #dc2626)'
138
+ }}
139
+ />
140
+ ) : null;
141
+
142
+ return (
143
+ <span
144
+ data-stock-status=""
145
+ data-stock-state={state}
146
+ {...partAttrs('pill', { empty: !inStock })}
147
+ style={{
148
+ display: 'inline-flex',
149
+ alignItems: 'center',
150
+ gap: '6px',
151
+ color: color || 'inherit'
152
+ }}
153
+ >
154
+ {indicatorPosition === 'before' && indicatorNode}
155
+ <span data-stock-label="" {...partAttrs('label', { empty: !inStock })}>
156
+ {label}
157
+ </span>
158
+ {indicatorPosition === 'after' && indicatorNode}
159
+ {showCount && inStock && hasCount && (
160
+ <span data-stock-count="" {...partAttrs('count')}>
161
+ {countTemplate.replace(/\{count\}/g, String(stockCount))}
162
+ </span>
163
+ )}
164
+ </span>
165
+ );
166
+ };
167
+
168
+ export default StockStatusBlock;