@akinon/pz-theme 2.0.37-rc.0 → 2.0.37
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 +7 -31
- package/package.json +4 -4
- package/src/__tests__/__snapshots__/facet-filters-block.test.tsx.snap +11 -0
- package/src/__tests__/custom-css.test.ts +181 -0
- package/src/__tests__/designer-chrome-clipping.test.tsx +497 -0
- package/src/__tests__/facet-filters-block.test.tsx +707 -0
- package/src/__tests__/item-template.test.tsx +184 -0
- package/src/__tests__/listing-controls.test.tsx +961 -0
- package/src/__tests__/part-style-scope.test.tsx +229 -0
- package/src/__tests__/part-styles.test.ts +311 -0
- package/src/__tests__/registry-parity.test.ts +42 -0
- package/src/__tests__/row-visibility.test.ts +672 -0
- package/src/__tests__/sort-select-page-cursor.test.tsx +336 -0
- package/src/__tests__/theme-block-html-id.test.tsx +167 -0
- package/src/blocks/__tests__/__snapshots__/product-gallery-block.test.tsx.snap +15 -0
- package/src/blocks/__tests__/accordion-block-state.test.tsx +201 -0
- package/src/blocks/__tests__/facet-filters-block.test.tsx +190 -0
- package/src/blocks/__tests__/listing-context-card-model.test.tsx +230 -0
- package/src/blocks/__tests__/listing-lookup-hardening.test.ts +80 -0
- package/src/blocks/__tests__/product-gallery-block.test.tsx +974 -0
- package/src/blocks/__tests__/product-grid-composition.test.tsx +570 -0
- package/src/blocks/__tests__/product-grid-lookups.test.tsx +230 -0
- package/src/blocks/__tests__/product-price-block.test.tsx +248 -0
- package/src/blocks/__tests__/product-recommendations-block.test.tsx +702 -0
- package/src/blocks/__tests__/share-block.test.tsx +392 -0
- package/src/blocks/__tests__/store-locator-block.test.tsx +691 -0
- package/src/blocks/__tests__/variant-selector-block.test.tsx +817 -0
- package/src/blocks/__tests__/variant-selector-golden.json +20 -0
- package/src/blocks/account-menu-block.tsx +264 -0
- package/src/blocks/add-to-cart-block.tsx +417 -0
- package/src/blocks/address-list-block.tsx +348 -0
- package/src/blocks/applied-filters-block.tsx +288 -0
- package/src/blocks/auth-form-block.tsx +295 -0
- package/src/blocks/basket-items-block.tsx +457 -0
- package/src/blocks/basket-summary-block.tsx +404 -0
- package/src/blocks/block-renderer-registry.tsx +94 -0
- package/src/blocks/change-password-form-block.tsx +240 -0
- package/src/blocks/checkout-steps-block.tsx +288 -0
- package/src/blocks/checkout-summary-block.tsx +449 -0
- package/src/blocks/countdown-block.tsx +182 -0
- package/src/blocks/coupon-list-block.tsx +292 -0
- package/src/blocks/facet-filters-block.tsx +1686 -0
- package/src/blocks/faq-accordion-block.tsx +358 -0
- package/src/blocks/favorite-button-block.tsx +252 -0
- package/src/blocks/favourites-grid-block.tsx +510 -0
- package/src/blocks/forgot-password-block.tsx +155 -0
- package/src/blocks/item-template-utils.ts +131 -0
- package/src/blocks/item-template.tsx +164 -0
- package/src/blocks/listing-breadcrumb-block.tsx +175 -0
- package/src/blocks/listing-context.ts +850 -0
- package/src/blocks/listing-pagination-block.tsx +923 -0
- package/src/blocks/listing-result-count-block.tsx +126 -0
- package/src/blocks/listing-title-block.tsx +115 -0
- package/src/blocks/oauth-buttons-block.tsx +205 -0
- package/src/blocks/order-detail-block.tsx +474 -0
- package/src/blocks/order-list-block.tsx +274 -0
- package/src/blocks/order-success-block.tsx +641 -0
- package/src/blocks/order-tracking-block.tsx +533 -0
- package/src/blocks/product-attributes-block.tsx +307 -0
- package/src/blocks/product-block-utils.tsx +639 -0
- package/src/blocks/product-breadcrumb-block.tsx +215 -0
- package/src/blocks/product-description-block.tsx +169 -0
- package/src/blocks/product-gallery-block.tsx +1523 -0
- package/src/blocks/product-grid-block.tsx +966 -0
- package/src/blocks/product-price-block.tsx +373 -0
- package/src/blocks/product-recommendations-block.tsx +1433 -0
- package/src/blocks/product-title-block.tsx +129 -0
- package/src/blocks/profile-form-block.tsx +297 -0
- package/src/blocks/recently-viewed-store.ts +201 -0
- package/src/blocks/register-form-block.tsx +362 -0
- package/src/blocks/share-block.tsx +667 -0
- package/src/blocks/size-guide-block.tsx +313 -0
- package/src/blocks/sort-select-block.tsx +636 -0
- package/src/blocks/stock-status-block.tsx +168 -0
- package/src/blocks/store-locator-block.tsx +1184 -0
- package/src/blocks/variant-selector-block.tsx +1110 -0
- package/src/sections/__tests__/tablet-raw-bucket-readers.test.tsx +186 -0
- 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;
|