@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,184 @@
|
|
|
1
|
+
import { buildIteratorBlock } from '../utils/iterator-utils';
|
|
2
|
+
import {
|
|
3
|
+
findItemTemplate,
|
|
4
|
+
hasItemTemplate,
|
|
5
|
+
publishesProductItems,
|
|
6
|
+
resolveItemProduct
|
|
7
|
+
} from '../blocks/item-template-utils';
|
|
8
|
+
import { sliceIteratorItems } from '../utils/iterator-utils';
|
|
9
|
+
import type { Block } from '../theme-block';
|
|
10
|
+
|
|
11
|
+
const templateChild = (overrides: Partial<Block> = {}): Block =>
|
|
12
|
+
({
|
|
13
|
+
id: 'tpl',
|
|
14
|
+
type: 'group',
|
|
15
|
+
label: 'Card',
|
|
16
|
+
order: 0,
|
|
17
|
+
hidden: false,
|
|
18
|
+
value: '',
|
|
19
|
+
properties: { role: { desktop: 'item-template' } },
|
|
20
|
+
styles: {},
|
|
21
|
+
blocks: [
|
|
22
|
+
{
|
|
23
|
+
id: 'tpl-name',
|
|
24
|
+
type: 'text',
|
|
25
|
+
label: 'Name',
|
|
26
|
+
order: 0,
|
|
27
|
+
hidden: false,
|
|
28
|
+
value: '',
|
|
29
|
+
properties: { dataBinding: 'item.name' },
|
|
30
|
+
styles: {},
|
|
31
|
+
blocks: []
|
|
32
|
+
}
|
|
33
|
+
],
|
|
34
|
+
...overrides
|
|
35
|
+
} as Block);
|
|
36
|
+
|
|
37
|
+
const listBlock = (children: Block[]): Block =>
|
|
38
|
+
({
|
|
39
|
+
id: 'grid',
|
|
40
|
+
type: 'product-grid',
|
|
41
|
+
label: 'Product grid',
|
|
42
|
+
order: 0,
|
|
43
|
+
hidden: false,
|
|
44
|
+
value: '',
|
|
45
|
+
properties: {},
|
|
46
|
+
styles: {},
|
|
47
|
+
blocks: children
|
|
48
|
+
} as Block);
|
|
49
|
+
|
|
50
|
+
describe('finding the item template', () => {
|
|
51
|
+
it('picks the child marked as the row layout', () => {
|
|
52
|
+
const block = listBlock([templateChild()]);
|
|
53
|
+
|
|
54
|
+
expect(hasItemTemplate(block)).toBe(true);
|
|
55
|
+
expect(findItemTemplate(block)?.id).toBe('tpl');
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('ignores children that are ordinary content', () => {
|
|
59
|
+
// Announcement messages and FAQ entries are children too; only a marked
|
|
60
|
+
// child means "repeat me per row".
|
|
61
|
+
const block = listBlock([
|
|
62
|
+
{
|
|
63
|
+
...templateChild(),
|
|
64
|
+
id: 'plain',
|
|
65
|
+
properties: {}
|
|
66
|
+
} as Block
|
|
67
|
+
]);
|
|
68
|
+
|
|
69
|
+
expect(hasItemTemplate(block)).toBe(false);
|
|
70
|
+
expect(findItemTemplate(block)).toBeNull();
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('reads the marker per breakpoint', () => {
|
|
74
|
+
const block = listBlock([
|
|
75
|
+
{
|
|
76
|
+
...templateChild(),
|
|
77
|
+
properties: { role: { desktop: '', mobile: 'item-template' } }
|
|
78
|
+
} as Block
|
|
79
|
+
]);
|
|
80
|
+
|
|
81
|
+
expect(hasItemTemplate(block, 'desktop')).toBe(false);
|
|
82
|
+
expect(hasItemTemplate(block, 'mobile')).toBe(true);
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
describe('repeating the template over caller-supplied rows', () => {
|
|
87
|
+
// The iterator repeats blocks[0], so the row layout is wrapped exactly the
|
|
88
|
+
// way ItemTemplateList wraps it — passing the card directly would repeat
|
|
89
|
+
// only its first element.
|
|
90
|
+
const expand = (items: Record<string, unknown>[]) =>
|
|
91
|
+
buildIteratorBlock({
|
|
92
|
+
iteratorBlock: {
|
|
93
|
+
...listBlock([templateChild()]),
|
|
94
|
+
isIterator: true,
|
|
95
|
+
blocks: [templateChild()]
|
|
96
|
+
} as Block,
|
|
97
|
+
items,
|
|
98
|
+
isDesigner: false
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('produces one clone per row', () => {
|
|
102
|
+
const result = expand([{ name: 'A' }, { name: 'B' }, { name: 'C' }]);
|
|
103
|
+
|
|
104
|
+
expect(result.blocks).toHaveLength(3);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('resolves item bindings inside each clone', () => {
|
|
108
|
+
const result = expand([{ name: 'Sneaker' }, { name: 'Boot' }]);
|
|
109
|
+
// The card is the repeated unit; the binding sits on the element inside it.
|
|
110
|
+
const values = (result.blocks || []).map(
|
|
111
|
+
(clone) => (clone.blocks || [])[0]?.value
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
expect(values).toEqual(['Sneaker', 'Boot']);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('keeps the template style identity on every clone', () => {
|
|
118
|
+
// The generated stylesheet only knows the template id; the DOM writes
|
|
119
|
+
// styleSourceId || id. Losing it drops every style authored on the row.
|
|
120
|
+
const result = expand([{ name: 'A' }, { name: 'B' }]);
|
|
121
|
+
|
|
122
|
+
(result.blocks || []).forEach((clone) => {
|
|
123
|
+
expect(clone.styleSourceId).toBe('tpl');
|
|
124
|
+
expect(clone.id).not.toBe('tpl');
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('does not repeat when the block is not an iterator', () => {
|
|
129
|
+
const plain = templateChild();
|
|
130
|
+
const result = buildIteratorBlock({
|
|
131
|
+
iteratorBlock: plain,
|
|
132
|
+
items: [{ name: 'A' }, { name: 'B' }]
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
expect(result).toBe(plain);
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
describe('the product row scope', () => {
|
|
140
|
+
it('opens only for lists whose rows are products', () => {
|
|
141
|
+
expect(publishesProductItems('product-grid')).toBe(true);
|
|
142
|
+
expect(publishesProductItems('favourites-grid')).toBe(true);
|
|
143
|
+
// An address row carries `pk` and a `name` too — scoping it would point a
|
|
144
|
+
// wishlist toggle at an address id.
|
|
145
|
+
expect(publishesProductItems('address-list')).toBe(false);
|
|
146
|
+
expect(publishesProductItems('product-attributes')).toBe(false);
|
|
147
|
+
expect(publishesProductItems(undefined)).toBe(false);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('falls through rather than shadowing the page product with nothing', () => {
|
|
151
|
+
// A row with no id cannot be acted on, so it must NOT claim the scope:
|
|
152
|
+
// claiming it would silence blocks that read the page product today — the
|
|
153
|
+
// recommendation strip on a PDP before its data arrives.
|
|
154
|
+
expect(resolveItemProduct('product-grid', {})).toBeNull();
|
|
155
|
+
expect(resolveItemProduct('product-grid', { name: 'No id' })).toBeNull();
|
|
156
|
+
expect(resolveItemProduct('product-grid', { pk: 'product-3' })).toBeNull();
|
|
157
|
+
expect(resolveItemProduct('product-grid', undefined)).toBeNull();
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('addresses the product, not the wishlist entry', () => {
|
|
161
|
+
// A favourites row carries both: `pk` is the entry, `productPk` the
|
|
162
|
+
// product behind it.
|
|
163
|
+
const scope = resolveItemProduct('favourites-grid', {
|
|
164
|
+
pk: 900,
|
|
165
|
+
productPk: 42,
|
|
166
|
+
name: 'Sneaker'
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
expect(scope?.pk).toBe(42);
|
|
170
|
+
expect(scope?.name).toBe('Sneaker');
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it('hands each clone the row that clone was built from', () => {
|
|
174
|
+
// With the count properties set, clone N is not items[N]. The scope reads
|
|
175
|
+
// the rows through the same slice, so the two cannot drift apart.
|
|
176
|
+
const rows = [{ n: 0 }, { n: 1 }, { n: 2 }, { n: 3 }];
|
|
177
|
+
const block = {
|
|
178
|
+
...templateChild(),
|
|
179
|
+
properties: { useIteratorCount: true, iteratorCount: 2, iteratorOffset: 1 }
|
|
180
|
+
} as Block;
|
|
181
|
+
|
|
182
|
+
expect(sliceIteratorItems(block, rows).items).toEqual([{ n: 1 }, { n: 2 }]);
|
|
183
|
+
});
|
|
184
|
+
});
|