@akinon/pz-theme 2.0.103-rc.0 → 2.0.103
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 +8 -33
- package/package.json +4 -4
- package/src/__tests__/collection-row-scope.test.tsx +187 -0
- package/src/__tests__/group-row-styles.test.tsx +110 -0
- package/src/__tests__/theme-typography-vars.test.tsx +125 -0
- package/src/blocks/__tests__/listing-row-swatches.test.ts +111 -0
- package/src/blocks/group-block.tsx +35 -1
- package/src/blocks/item-template-utils.ts +34 -0
- package/src/blocks/item-template.tsx +6 -1
- package/src/blocks/listing-context.ts +17 -622
- package/src/blocks/listing-row.ts +692 -0
- package/src/components/with-designer-features.tsx +6 -0
- package/src/sections/__tests__/posts-slider-row-visibility.test.tsx +147 -0
- package/src/sections/posts-slider-section.tsx +23 -2
- package/src/theme-block.tsx +63 -2
- package/src/theme-placeholder-client.tsx +4 -3
- package/src/theme-placeholder-wrapper.tsx +61 -5
- package/src/utils/__tests__/property-switches.test.tsx +250 -0
- package/src/utils/index.ts +55 -3
- package/src/utils/iterator-price-shapes.test.ts +465 -0
- package/src/utils/iterator-utils.ts +371 -35
- package/src/utils/property-switches.ts +230 -0
|
@@ -0,0 +1,465 @@
|
|
|
1
|
+
import { buildIteratorBlock, expandNestedIterators } from './iterator-utils';
|
|
2
|
+
import type { Block } from '../theme-block';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* ZERO-4829 — the editor previews a collection with Omnitron's item rows,
|
|
6
|
+
* whose money sits under `active_price`; the storefront fetches commerce rows
|
|
7
|
+
* with `price` flat. A card bound to `item.price` previewed with EMPTY prices,
|
|
8
|
+
* because the price branch was reached only when the bound path itself
|
|
9
|
+
* resolved — the pair reader that handles both shapes never got asked.
|
|
10
|
+
*/
|
|
11
|
+
const priceText = (): Block => ({
|
|
12
|
+
id: 'price',
|
|
13
|
+
type: 'text',
|
|
14
|
+
label: 'Price',
|
|
15
|
+
value: '',
|
|
16
|
+
properties: { tag: 'p', dataBinding: 'item.price' },
|
|
17
|
+
styles: {},
|
|
18
|
+
blocks: []
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const iterator = (): Block => ({
|
|
22
|
+
id: 'it',
|
|
23
|
+
type: 'group',
|
|
24
|
+
label: 'Iterator',
|
|
25
|
+
isIterator: true,
|
|
26
|
+
properties: { tag: 'div', iteratorDataPath: 'products', iteratorCount: 2 },
|
|
27
|
+
styles: {},
|
|
28
|
+
blocks: [
|
|
29
|
+
{
|
|
30
|
+
id: 'card',
|
|
31
|
+
type: 'group',
|
|
32
|
+
label: 'Card',
|
|
33
|
+
properties: { tag: 'div' },
|
|
34
|
+
styles: {},
|
|
35
|
+
blocks: [priceText()]
|
|
36
|
+
}
|
|
37
|
+
]
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const source = (products: Record<string, unknown>[]) => ({
|
|
41
|
+
id: 'ds',
|
|
42
|
+
name: 'c',
|
|
43
|
+
type: 'collection',
|
|
44
|
+
details: { collection: { pk: 1, productLimit: 2, products } }
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const priceValueOf = (expanded: Block, index: number): string =>
|
|
48
|
+
String(expanded.blocks?.[index]?.blocks?.[0]?.value ?? '');
|
|
49
|
+
|
|
50
|
+
describe('a price binding on a collection row', () => {
|
|
51
|
+
it('answers from `active_price` when the row carries no flat price (editor preview rows)', () => {
|
|
52
|
+
const expanded = buildIteratorBlock({
|
|
53
|
+
iteratorBlock: iterator(),
|
|
54
|
+
sectionDataSource: source([
|
|
55
|
+
{
|
|
56
|
+
pk: 1,
|
|
57
|
+
name: 'A',
|
|
58
|
+
active_price: {
|
|
59
|
+
price: '3499.00',
|
|
60
|
+
retail_price: '0.00',
|
|
61
|
+
currency_type: 'try'
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
pk: 2,
|
|
66
|
+
name: 'B',
|
|
67
|
+
active_price: {
|
|
68
|
+
price: '2999.00',
|
|
69
|
+
retail_price: '3999.00',
|
|
70
|
+
currency_type: 'try'
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
]),
|
|
74
|
+
pageContext: null
|
|
75
|
+
} as never);
|
|
76
|
+
|
|
77
|
+
expect(priceValueOf(expanded, 0)).toBe('3.499 TL');
|
|
78
|
+
// A discount strikes the list price into the same text.
|
|
79
|
+
expect(priceValueOf(expanded, 1)).toContain('line-through');
|
|
80
|
+
expect(priceValueOf(expanded, 1)).toContain('3.999 TL');
|
|
81
|
+
expect(priceValueOf(expanded, 1)).toContain('2.999 TL');
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('still answers from the flat fields the storefront rows carry', () => {
|
|
85
|
+
const expanded = buildIteratorBlock({
|
|
86
|
+
iteratorBlock: iterator(),
|
|
87
|
+
sectionDataSource: source([
|
|
88
|
+
{
|
|
89
|
+
pk: 1,
|
|
90
|
+
name: 'A',
|
|
91
|
+
price: '3499.00',
|
|
92
|
+
retail_price: '',
|
|
93
|
+
currency_type: 'try'
|
|
94
|
+
}
|
|
95
|
+
]),
|
|
96
|
+
pageContext: null
|
|
97
|
+
} as never);
|
|
98
|
+
|
|
99
|
+
expect(priceValueOf(expanded, 0)).toBe('3.499 TL');
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('leaves the text empty when the row has no price in either shape', () => {
|
|
103
|
+
const expanded = buildIteratorBlock({
|
|
104
|
+
iteratorBlock: iterator(),
|
|
105
|
+
sectionDataSource: source([{ pk: 1, name: 'A' }]),
|
|
106
|
+
pageContext: null
|
|
107
|
+
} as never);
|
|
108
|
+
|
|
109
|
+
expect(priceValueOf(expanded, 0)).toBe('');
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* The Product Card puts the list price and the current price in two texts so
|
|
115
|
+
* each can carry its own colour; a text bound to a price path therefore says
|
|
116
|
+
* which PART of the price it prints. And a collection row is published in the
|
|
117
|
+
* listing row's shape on top of its own, so the same card binds `item.url`,
|
|
118
|
+
* `item.retailPrice`, `item.hasDiscount`, `item.attributes`, `item.colors`
|
|
119
|
+
* over a grid row and a collection row alike.
|
|
120
|
+
*/
|
|
121
|
+
const text = (
|
|
122
|
+
id: string,
|
|
123
|
+
properties: Record<string, unknown>,
|
|
124
|
+
styles: Record<string, unknown> = {}
|
|
125
|
+
): Block => ({
|
|
126
|
+
id,
|
|
127
|
+
type: 'text',
|
|
128
|
+
label: id,
|
|
129
|
+
value: '',
|
|
130
|
+
properties: { tag: 'p', ...properties },
|
|
131
|
+
styles,
|
|
132
|
+
blocks: []
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
const cardIterator = (
|
|
136
|
+
rootProperties: Record<string, unknown>,
|
|
137
|
+
children: Block[]
|
|
138
|
+
): Block => ({
|
|
139
|
+
id: 'it',
|
|
140
|
+
type: 'group',
|
|
141
|
+
label: 'Iterator',
|
|
142
|
+
isIterator: true,
|
|
143
|
+
properties: { tag: 'div', iteratorDataPath: 'products', iteratorCount: 2 },
|
|
144
|
+
styles: {},
|
|
145
|
+
blocks: [
|
|
146
|
+
{
|
|
147
|
+
id: 'card',
|
|
148
|
+
type: 'group',
|
|
149
|
+
label: 'Card',
|
|
150
|
+
properties: { tag: 'div', ...rootProperties },
|
|
151
|
+
styles: {},
|
|
152
|
+
blocks: children
|
|
153
|
+
}
|
|
154
|
+
]
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
const valuesOf = (expanded: Block, index: number): Record<string, unknown> =>
|
|
158
|
+
Object.fromEntries(
|
|
159
|
+
(expanded.blocks?.[index]?.blocks || []).map((child) => [
|
|
160
|
+
child.id,
|
|
161
|
+
child.value
|
|
162
|
+
])
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
const omnitronRow = (pk: number, price: string, retail: string) => ({
|
|
166
|
+
pk,
|
|
167
|
+
name: `Product ${pk}`,
|
|
168
|
+
productimage_set: [{ image: `/img-${pk}.jpg` }],
|
|
169
|
+
attributes: { badge2: pk === 1 ? 'YENİ SEZON' : '', integration_renk: '457' },
|
|
170
|
+
extra_data: {
|
|
171
|
+
variants: [
|
|
172
|
+
{
|
|
173
|
+
attribute_key: 'integration_renk',
|
|
174
|
+
options: [
|
|
175
|
+
{
|
|
176
|
+
label: '457',
|
|
177
|
+
is_selectable: true,
|
|
178
|
+
product: { pk, absolute_url: `/p-${pk}/` }
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
label: '200',
|
|
182
|
+
is_selectable: true,
|
|
183
|
+
product: { pk: pk + 10, absolute_url: `/p-${pk + 10}/` }
|
|
184
|
+
}
|
|
185
|
+
]
|
|
186
|
+
}
|
|
187
|
+
]
|
|
188
|
+
},
|
|
189
|
+
active_price: { price, retail_price: retail, currency_type: 'try' }
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
describe('pricePart on a text bound to a price path', () => {
|
|
193
|
+
const children = [
|
|
194
|
+
text('full', { dataBinding: 'item.price' }),
|
|
195
|
+
text('current', { dataBinding: 'item.price', pricePart: 'current' }),
|
|
196
|
+
text('retail', { dataBinding: 'item.price', pricePart: 'retail' }),
|
|
197
|
+
text('retailOnRetailPath', {
|
|
198
|
+
dataBinding: 'item.active_price.retail_price',
|
|
199
|
+
pricePart: { desktop: 'retail' }
|
|
200
|
+
})
|
|
201
|
+
];
|
|
202
|
+
|
|
203
|
+
it('prints one part each, and nothing for a figure the row does not have', () => {
|
|
204
|
+
const expanded = buildIteratorBlock({
|
|
205
|
+
iteratorBlock: cardIterator({}, children),
|
|
206
|
+
sectionDataSource: source([
|
|
207
|
+
omnitronRow(1, '2999.00', '3999.00'),
|
|
208
|
+
omnitronRow(2, '3499.00', '0.00')
|
|
209
|
+
]),
|
|
210
|
+
pageContext: null
|
|
211
|
+
} as never);
|
|
212
|
+
|
|
213
|
+
const discounted = valuesOf(expanded, 0);
|
|
214
|
+
expect(discounted.full).toContain('line-through');
|
|
215
|
+
expect(discounted.current).toBe('2.999 TL');
|
|
216
|
+
expect(discounted.retail).toBe('3.999 TL');
|
|
217
|
+
expect(discounted.retailOnRetailPath).toBe('3.999 TL');
|
|
218
|
+
|
|
219
|
+
const plain = valuesOf(expanded, 1);
|
|
220
|
+
expect(plain.full).toBe('3.499 TL');
|
|
221
|
+
expect(plain.current).toBe('3.499 TL');
|
|
222
|
+
expect(plain.retail).toBe('');
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it('formats every part with the money format authored on the card root', () => {
|
|
226
|
+
const expanded = buildIteratorBlock({
|
|
227
|
+
iteratorBlock: cardIterator(
|
|
228
|
+
{
|
|
229
|
+
decimals: { desktop: 'always' },
|
|
230
|
+
currencyDisplay: { desktop: 'custom' },
|
|
231
|
+
currencyText: { desktop: 'TL' },
|
|
232
|
+
currencyPosition: { desktop: 'after' }
|
|
233
|
+
},
|
|
234
|
+
[...children, text('list', { dataBinding: 'item.retailPrice' })]
|
|
235
|
+
),
|
|
236
|
+
sectionDataSource: source([omnitronRow(1, '2999.00', '3999.00')]),
|
|
237
|
+
pageContext: null
|
|
238
|
+
} as never);
|
|
239
|
+
|
|
240
|
+
const values = valuesOf(expanded, 0);
|
|
241
|
+
expect(values.current).toBe('2.999,00 TL');
|
|
242
|
+
expect(values.retail).toBe('3.999,00 TL');
|
|
243
|
+
expect(values.list).toBe('3.999,00 TL');
|
|
244
|
+
expect(values.full).toContain('3.999,00 TL');
|
|
245
|
+
expect(values.full).toContain('2.999,00 TL');
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
describe('a collection row published in the listing row shape', () => {
|
|
250
|
+
const children = [
|
|
251
|
+
text('url', { dataBinding: 'item.url' }),
|
|
252
|
+
text('image', { dataBinding: 'item.image' }),
|
|
253
|
+
text('list', { dataBinding: 'item.retailPrice' }),
|
|
254
|
+
text('percent', { dataBinding: '-%{{item.discountPercent}}' }),
|
|
255
|
+
text('badge', { dataBinding: 'item.attributes.badge2' }),
|
|
256
|
+
text('colours', { dataBinding: '{{item.colorCount}} colours' }),
|
|
257
|
+
text('firstSwatch', { dataBinding: 'item.colors[1].url' }),
|
|
258
|
+
text('struck', {
|
|
259
|
+
dataBinding: 'item.retailPrice',
|
|
260
|
+
visibleWhenField: 'item.hasDiscount',
|
|
261
|
+
visibleWhenMode: 'truthy'
|
|
262
|
+
})
|
|
263
|
+
];
|
|
264
|
+
|
|
265
|
+
it('derives url, image, list price, discount, attributes and colours from an Omnitron row', () => {
|
|
266
|
+
const expanded = buildIteratorBlock({
|
|
267
|
+
iteratorBlock: cardIterator({}, children),
|
|
268
|
+
sectionDataSource: source([
|
|
269
|
+
omnitronRow(1, '2999.00', '3999.00'),
|
|
270
|
+
omnitronRow(2, '3499.00', '0.00')
|
|
271
|
+
]),
|
|
272
|
+
pageContext: null
|
|
273
|
+
} as never);
|
|
274
|
+
|
|
275
|
+
const discounted = valuesOf(expanded, 0);
|
|
276
|
+
// An Omnitron item row carries no product URL of its own; the link falls
|
|
277
|
+
// back to '#' the way a listing row without one does.
|
|
278
|
+
expect(discounted.url).toBe('#');
|
|
279
|
+
expect(discounted.image).toBe('/img-1.jpg');
|
|
280
|
+
expect(discounted.list).toBe('3.999 TL');
|
|
281
|
+
expect(discounted.percent).toBe('-%25');
|
|
282
|
+
expect(discounted.badge).toBe('YENİ SEZON');
|
|
283
|
+
expect(discounted.colours).toBe('2 colours');
|
|
284
|
+
expect(discounted.firstSwatch).toBe('/p-11/');
|
|
285
|
+
|
|
286
|
+
const struck = expanded.blocks?.map((clone) =>
|
|
287
|
+
clone.blocks?.find((child) => child.id === 'struck')?.hidden
|
|
288
|
+
);
|
|
289
|
+
// Row visibility reads the derived flag: struck on the discounted row only.
|
|
290
|
+
expect(struck).toEqual([undefined, true]);
|
|
291
|
+
expect(valuesOf(expanded, 1).list).toBe('');
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
it('derives the url and the flat money from a commerce row (what the storefront fetches)', () => {
|
|
295
|
+
const expanded = buildIteratorBlock({
|
|
296
|
+
iteratorBlock: cardIterator({}, [
|
|
297
|
+
text('url', { dataBinding: 'item.url' }),
|
|
298
|
+
text('list', { dataBinding: 'item.retailPrice' }),
|
|
299
|
+
text('current', { dataBinding: 'item.price', pricePart: 'current' }),
|
|
300
|
+
text('flag', { dataBinding: '{{item.hasDiscount}}' })
|
|
301
|
+
]),
|
|
302
|
+
sectionDataSource: source([
|
|
303
|
+
{
|
|
304
|
+
pk: 3,
|
|
305
|
+
name: 'C',
|
|
306
|
+
absolute_url: '/p-3/',
|
|
307
|
+
price: '2999.00',
|
|
308
|
+
retail_price: '3999.00',
|
|
309
|
+
currency_type: 'try'
|
|
310
|
+
}
|
|
311
|
+
]),
|
|
312
|
+
pageContext: null
|
|
313
|
+
} as never);
|
|
314
|
+
|
|
315
|
+
expect(valuesOf(expanded, 0)).toEqual({
|
|
316
|
+
url: '/p-3/',
|
|
317
|
+
list: '3.999 TL',
|
|
318
|
+
current: '2.999 TL',
|
|
319
|
+
flag: 'true'
|
|
320
|
+
});
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
it('fills swatch colours from a table authored on the card root', () => {
|
|
324
|
+
const expanded = buildIteratorBlock({
|
|
325
|
+
iteratorBlock: cardIterator({ swatchMap: '457|#2d5da1\n200|#d9c8a9' }, [
|
|
326
|
+
text('hex', { dataBinding: 'item.colors[1].color' })
|
|
327
|
+
]),
|
|
328
|
+
sectionDataSource: source([omnitronRow(1, '2999.00', '3999.00')]),
|
|
329
|
+
pageContext: null
|
|
330
|
+
} as never);
|
|
331
|
+
|
|
332
|
+
expect(valuesOf(expanded, 0).hex).toBe('#d9c8a9');
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
it('never replaces a field the row already carries', () => {
|
|
336
|
+
const expanded = buildIteratorBlock({
|
|
337
|
+
iteratorBlock: cardIterator({}, [
|
|
338
|
+
text('name', { dataBinding: 'item.name' }),
|
|
339
|
+
text('url', { dataBinding: 'item.url' })
|
|
340
|
+
]),
|
|
341
|
+
sectionDataSource: source([
|
|
342
|
+
{ ...omnitronRow(1, '2999.00', ''), url: '/custom/', absolute_url: '/x/' }
|
|
343
|
+
]),
|
|
344
|
+
pageContext: null
|
|
345
|
+
} as never);
|
|
346
|
+
|
|
347
|
+
expect(valuesOf(expanded, 0)).toEqual({ name: 'Product 1', url: '/custom/' });
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
it('leaves the scope product as the raw row', () => {
|
|
351
|
+
const expanded = buildIteratorBlock({
|
|
352
|
+
iteratorBlock: cardIterator({}, [text('name', { dataBinding: 'item.name' })]),
|
|
353
|
+
sectionDataSource: source([omnitronRow(7, '2999.00', '')]),
|
|
354
|
+
pageContext: null
|
|
355
|
+
} as never);
|
|
356
|
+
|
|
357
|
+
expect(expanded.blocks?.[0]?.itemScopeProduct).toMatchObject({ pk: 7 });
|
|
358
|
+
expect('retailPrice' in (expanded.blocks?.[0]?.itemScopeProduct || {})).toBe(
|
|
359
|
+
false
|
|
360
|
+
);
|
|
361
|
+
});
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
describe('a repeat inside a collection row', () => {
|
|
365
|
+
const strip = (): Block => ({
|
|
366
|
+
id: 'strip',
|
|
367
|
+
type: 'group',
|
|
368
|
+
label: 'Swatches',
|
|
369
|
+
isIterator: true,
|
|
370
|
+
properties: {
|
|
371
|
+
tag: 'div',
|
|
372
|
+
iteratorDataPath: 'item.colors',
|
|
373
|
+
useIteratorCount: true,
|
|
374
|
+
iteratorCount: 3
|
|
375
|
+
},
|
|
376
|
+
styles: {},
|
|
377
|
+
blocks: [
|
|
378
|
+
{
|
|
379
|
+
id: 'chip',
|
|
380
|
+
type: 'group',
|
|
381
|
+
label: 'Chip',
|
|
382
|
+
properties: {
|
|
383
|
+
tag: 'a',
|
|
384
|
+
dataBinding: 'item.url',
|
|
385
|
+
dataBindingTarget: 'href'
|
|
386
|
+
},
|
|
387
|
+
styles: {
|
|
388
|
+
'background-image': {
|
|
389
|
+
desktop: 'linear-gradient({{item.color}}, {{item.color}})'
|
|
390
|
+
}
|
|
391
|
+
},
|
|
392
|
+
blocks: []
|
|
393
|
+
}
|
|
394
|
+
]
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
it('draws its product colours — capped by its count — and never treats them as products', () => {
|
|
398
|
+
const outer = buildIteratorBlock({
|
|
399
|
+
iteratorBlock: cardIterator({ swatchMap: '457|#2d5da1\n200|#d9c8a9' }, [
|
|
400
|
+
strip()
|
|
401
|
+
]),
|
|
402
|
+
sectionDataSource: source([omnitronRow(1, '2999.00', '3999.00')]),
|
|
403
|
+
pageContext: null
|
|
404
|
+
} as never);
|
|
405
|
+
|
|
406
|
+
const card = expandNestedIterators(outer.blocks?.[0] as Block);
|
|
407
|
+
const chips = card.blocks?.[0]?.blocks || [];
|
|
408
|
+
|
|
409
|
+
expect(chips).toHaveLength(2);
|
|
410
|
+
expect(chips.map((chip) => chip.properties?.href)).toEqual(['/p-1/', '/p-11/']);
|
|
411
|
+
expect(chips.map((chip) => chip.styles?.['background-image']?.desktop)).toEqual([
|
|
412
|
+
'linear-gradient(#2d5da1, #2d5da1)',
|
|
413
|
+
'linear-gradient(#d9c8a9, #d9c8a9)'
|
|
414
|
+
]);
|
|
415
|
+
// The colours are not products: no scope is opened around a chip.
|
|
416
|
+
for (const chip of chips) expect('itemScopeProduct' in chip).toBe(false);
|
|
417
|
+
expect(card.blocks?.[0]?.properties).not.toHaveProperty('iteratorScopedItems');
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
it('on the canvas, a product without colours still gets the count floor to arrange', () => {
|
|
421
|
+
const outer = buildIteratorBlock({
|
|
422
|
+
iteratorBlock: cardIterator({}, [strip()]),
|
|
423
|
+
sectionDataSource: source([{ ...omnitronRow(1, '2999.00', ''), extra_data: {} }]),
|
|
424
|
+
pageContext: null,
|
|
425
|
+
isDesigner: true
|
|
426
|
+
} as never);
|
|
427
|
+
|
|
428
|
+
const [clone] = (outer.blocks || []).map((c) => expandNestedIterators(c, { isDesigner: true }));
|
|
429
|
+
// Three placeholder chips (the strip's iteratorCount), none linking anywhere.
|
|
430
|
+
expect(clone.blocks?.[0]?.blocks).toHaveLength(3);
|
|
431
|
+
expect(clone.blocks?.[0]?.blocks?.every((chip) => !chip.properties?.href)).toBe(true);
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
it('draws nothing for a product without colours, and expands an already pre-expanded strip', () => {
|
|
435
|
+
const outer = buildIteratorBlock({
|
|
436
|
+
iteratorBlock: cardIterator({}, [
|
|
437
|
+
// What the section walk leaves behind when it met the strip before
|
|
438
|
+
// the row existed: one placeholder clone in place of the template.
|
|
439
|
+
{
|
|
440
|
+
...strip(),
|
|
441
|
+
blocks: [
|
|
442
|
+
{
|
|
443
|
+
...(strip().blocks as Block[])[0],
|
|
444
|
+
id: 'chip-clone-0',
|
|
445
|
+
styleSourceId: 'chip'
|
|
446
|
+
}
|
|
447
|
+
]
|
|
448
|
+
}
|
|
449
|
+
]),
|
|
450
|
+
sectionDataSource: source([
|
|
451
|
+
{ ...omnitronRow(1, '2999.00', ''), extra_data: {} },
|
|
452
|
+
omnitronRow(2, '2999.00', '')
|
|
453
|
+
]),
|
|
454
|
+
pageContext: null
|
|
455
|
+
} as never);
|
|
456
|
+
|
|
457
|
+
const [noColours, twoColours] = (outer.blocks || []).map((clone) =>
|
|
458
|
+
expandNestedIterators(clone)
|
|
459
|
+
);
|
|
460
|
+
expect(noColours.blocks?.[0]?.blocks).toHaveLength(0);
|
|
461
|
+
expect(
|
|
462
|
+
twoColours.blocks?.[0]?.blocks?.map((chip) => chip.styleSourceId)
|
|
463
|
+
).toEqual(['chip', 'chip']);
|
|
464
|
+
});
|
|
465
|
+
});
|